@shogun-sdk/money-legos v1.3.33
@shogun-sdk/money-legos
Legos SDK for building with Shogun's money legos. This package allows you to purchase NFTs on Bera Chain using any token from any blockchain.
Overview
The Shogun Money Legos SDK provides a comprehensive set of tools for interacting with Shogun's cross-chain infrastructure. It enables:
- Cross-chain NFT Purchases: Buy NFTs on Bera Chain using tokens from any supported blockchain
- Cross-chain Token Swaps: Swap tokens between different EVM chains and Solana
- Balance Checking: Check token balances across multiple chains
- Transaction Signing: Sign and execute transactions on various blockchains
The SDK is built with TypeScript and provides a type-safe API for interacting with Shogun's services.
Installation
npm install @shogun-sdk/money-legos
# or
yarn add @shogun-sdk/money-legos
# or
pnpm add @shogun-sdk/money-legos
Configuration
The SDK requires an API key for authentication. You can obtain this from the Shogun dashboard.
RPC URLs
The SDK provides multiple ways to configure RPC URLs:
1. Using the Initialization Function (Recommended)
You can explicitly set RPC URLs using the initialization function:
import { init } from '@shogun-sdk/money-legos';
// Initialize the SDK with custom RPC URLs
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'],
}
});
// Now use the SDK as usual
import { LegoClient } from '@shogun-sdk/money-legos';
const legoClient = new LegoClient({ apiKey: 'YOUR_API_KEY' });
This approach is recommended for Next.js applications and gives you the most control over RPC URLs.
2. Using Environment Variables
The SDK can also use environment variables for RPC URLs. You can configure them as follows:
For Node.js applications:
Create a .env
file in your project root:
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
Then load the environment variables using dotenv:
import 'dotenv/config';
// Your application code
For React/Vite applications:
For Vite applications, you'll need to use a plugin like vite-plugin-env-compatible
to expose environment variables to the client. Create a .env
file in your project root:
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
Then configure your Vite application to expose these variables:
// 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_',
}),
],
});
For Next.js applications:
Next.js has a specific way of handling environment variables. Variables prefixed with NEXT_PUBLIC_
are exposed to the browser. Create a .env.local
file in your project root:
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
For server-side code, you can use regular environment variables without the NEXT_PUBLIC_
prefix:
RPC_URL_1=https://your-server-side-ethereum-rpc-url
RPC_URL_8453=https://your-server-side-base-rpc-url
# ... and so on
Fallback RPCs
If no environment variables or custom RPC URLs are provided, the SDK will use public RPC endpoints as fallbacks. However, for production applications, it's recommended to use your own RPC endpoints for better reliability and performance.
Configuration
import { LegoClient, OneShotClient, ShogunBalancesApiClient } from '@shogun-sdk/money-legos';
// Initialize the Lego client for NFT purchases
const legoClient = new LegoClient({
apiKey: 'YOUR_API_KEY', // Get this from Shogun dashboard
});
// Initialize the OneShot client for cross-chain swaps
const oneShotClient = new OneShotClient(
'YOUR_API_KEY', // Get this from Shogun dashboard
'SHOGUN_API_URL' // API endpoint
);
// Initialize the Balances client for checking token balances
const balancesClient = new ShogunBalancesApiClient('YOUR_API_KEY');
Supported Chains
The SDK supports multiple blockchain networks:
import {
ETHEREUM_CHAIN_ID,
BASE_CHAIN_ID,
ARBITRUM_CHAIN_ID,
BSC_CHAIN_ID,
BERA_CHAIN_ID,
SOLANA_CHAIN_ID,
SONIC_CHAIN_ID
} from '@shogun-sdk/money-legos';
// Chain IDs
console.log(ETHEREUM_CHAIN_ID); // 1
console.log(BASE_CHAIN_ID); // 8453
console.log(ARBITRUM_CHAIN_ID); // 42161
console.log(BSC_CHAIN_ID); // 56
console.log(BERA_CHAIN_ID); // 80094
console.log(SOLANA_CHAIN_ID); // 7565164
console.log(SONIC_CHAIN_ID); // 146
// List of all supported chains
import { getSupportedChains , CHAIN_MAP } from '@shogun-sdk/money-legos';
Quick Start
1. Install the package
npm install @shogun-sdk/money-legos
# or
yarn add @shogun-sdk/money-legos
2. Initialize the SDK with RPC URLs (Recommended)
import { init } from '@shogun-sdk/money-legos';
// Initialize with your RPC URLs
init({
rpcUrls: {
1: ['https://your-ethereum-rpc-url'],
8453: ['https://your-base-rpc-url'],
// Add other chains as needed
}
});
3. Initialize the Lego Client
import { LegoClient } from '@shogun-sdk/money-legos';
// Create a new client instance
const legoClient = new LegoClient({
apiKey: 'YOUR_API_KEY', // Get this from Shogun dashboard
});
4. Basic Usage Example
// Example: Purchase an NFT using ETH
async function purchaseNFT() {
try {
const result = await legoClient.MagicEden.SwapForNFT({
items: [{
address: '0x7E0b0363804C6C79AAb9aB51850bF8F3D561f868', // NFT contract address
tokenId: '42' // The NFT token ID you want to purchase
}],
token: {
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH address
decimals: 18,
chainId: 1 // Ethereum mainnet
},
userAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' // Your wallet address
});
if (result.status) {
console.log('Success! Transaction steps:', result.data.steps);
// Execute the transaction steps...
} else {
console.error('Failed to fetch data:', result.error);
}
} catch (error) {
console.error('Error:', error);
}
}
Using with Viem Client
Here's a complete example of how to execute transactions using Viem:
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
import { LegoClient } from '@shogun-sdk/money-legos';
async function setupBuyNftWithViem() {
// 1. Initialize the Lego client
const legoClient = new LegoClient({
apiKey: "your api key here",
});
// 2. Set up your wallet
const account = privateKeyToAccount("0x..."); // Your private key
const client = createWalletClient({
chain: base,
transport: http(),
account,
});
try {
// 3. Fetch the purchase data
const { data } = await legoClient.MagicEden.SwapForNFT({
token: {
address: "0x0000000000000000000000000000000000000000", // ETH
decimals: 18,
chainId: 8453, // Base chain
},
items: [
{
address: "0x72d876d9cdf4001b836f8e47254d0551eda2eebb", // NFT contract
tokenId: "32", // NFT ID
},
],
userAddress: account.address,
});
if (!data) {
console.error("No data returned from MagicEden.SwapForNFT");
return;
}
// 4. Execute each transaction step
const { steps } = data;
for (const step of steps) {
console.log(`Executing step: ${step.description || 'Unknown 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("Error during NFT purchase:", error);
}
}
Transaction Signing Examples
EVM Transaction Signing
Here's how to sign and execute EVM transactions using the OneShot client:
import { OneShotClient, QuoteTypes } from '@shogun-sdk/money-legos';
import { createWalletClient, http, getPublicClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { mainnet } from 'viem/chains';
// Initialize the OneShot client
const oneShotClient = new OneShotClient(
'YOUR_API_KEY',
'SHOGUN_API_URL'
);
// Set up your wallet
const account = privateKeyToAccount('0x...'); // Your private key
const walletClient = createWalletClient({
account,
chain: mainnet,
transport: http()
});
// Function to handle EVM transactions
const handleEVMTransaction = async (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 });
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,
});
// Increase estimated gas by 20%
const gas = BigInt(Math.ceil(Number(estimatedGas) * 1.2));
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,
});
const swapPlacementTimestamp = Date.now();
const tx = await provider.waitForTransactionReceipt({
hash,
retryCount: 5,
confirmations: 1,
});
const swapExecutionTimestamp = Date.now();
if (tx.status !== 'success') {
throw new Error(`EVM transaction failed: ${tx.status}`);
}
return { hash, swapPlacementTimestamp, swapExecutionTimestamp };
} catch (error) {
throw error;
}
};
// Example usage
async function performEVMTransaction() {
try {
// Get a quote for a cross-chain swap
const quote = await oneShotClient.fetchQuote({
srcChain: 1, // Ethereum mainnet
destChain: 8453, // Base chain
srcToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
destToken: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', // WBTC
amount: '1000000000000000000', // 1 ETH (in wei)
senderAddress: account.address,
slippage: 0.5, // 0.5% slippage tolerance
});
if (!quote.status) {
console.error('Failed to get quote:', quote.error);
return;
}
// Execute the transaction
const result = await handleEVMTransaction(quote.data, walletClient, mainnet);
console.log('Transaction result:', result);
} catch (error) {
console.error('Error performing transaction:', error);
}
}
Solana Transaction Signing
Here's how to sign and execute Solana transactions:
import { OneShotClient, QuoteTypes } from '@shogun-sdk/money-legos';
import { VersionedTransaction } from '@solana/web3.js';
import bs58 from 'bs58';
import {
sendTransactionUsingJito,
sendBundleUsingJito,
isValidSolanaSignature,
checkTransactionConfirmation,
confirmTransaction,
} from '@shogun-sdk/money-legos';
// Initialize the OneShot client
const oneShotClient = new OneShotClient(
'YOUR_API_KEY',
'SHOGUN_API_URL'
);
// Function to handle Solana transactions
const handleSolanaTransaction = async (
quote: QuoteTypes,
address: string,
client: YourWalletSigner
): Promise<{ transactionHash: string; swapPlacementTimestamp: number; swapExecutionTimestamp: number } | null> => {
if (!Array.isArray(quote.calldatas)) return null;
const transactions = await Promise.all(
quote.calldatas.map(async (calldata) => {
const messageBuffer = Buffer.from(calldata.data, 'base64');
return VersionedTransaction.deserialize(messageBuffer);
}),
);
const signedTransactions = await client.signAllTransactions!(
transactions,
address,
);
if (!signedTransactions) {
throw new Error('Failed to sign transactions');
}
const base58Transactions = signedTransactions.map((tx) => bs58.encode(tx.serialize()));
if (quote.calldatas.length === 1) {
return handleSingleSolanaTransaction(base58Transactions[0]);
} else {
return handleSolanaBundleTransaction(base58Transactions, transactions[0]);
}
};
// Function to handle a single Solana transaction
const handleSingleSolanaTransaction = async (base58Transaction: string): Promise<{ transactionHash: string; swapPlacementTimestamp: number; swapExecutionTimestamp: number }> => {
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 };
};
// Function to handle a bundle of Solana transactions
const handleSolanaBundleTransaction = async (
base58Transactions: string[],
firstTransaction: VersionedTransaction,
): Promise<{ transactionHash: string; swapPlacementTimestamp: number; swapExecutionTimestamp: number }> => {
// Get the signature from the first transaction
const transactionSignature = firstTransaction?.signatures?.[0];
if (!transactionSignature) {
throw new Error('Missing or invalid transaction signature');
}
// Convert signature to hash
const transactionHash = bs58.encode(transactionSignature);
if (!isValidSolanaSignature(transactionHash)) {
throw new Error('Invalid transaction signature format');
}
// Send the bundle to Jito
const bundleID = await sendBundleUsingJito(base58Transactions);
const swapPlacementTimestamp = Date.now();
// Wait for confirmation
const confirmed = await checkTransactionConfirmation(transactionHash);
const swapExecutionTimestamp = Date.now();
if (!confirmed) {
throw new Error('Bundle transaction failed to confirm');
}
return { transactionHash, swapPlacementTimestamp, swapExecutionTimestamp };
};
// Example usage
async function performSolanaTransaction() {
try {
// Get a quote for a cross-chain swap
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: 'your_solana_address',
slippage: 0.5, // 0.5% slippage tolerance
});
if (!quote.status) {
console.error('Failed to get quote:', quote.error);
return;
}
// Execute the transaction
const result = await handleSolanaTransaction(
quote.data,
'your_solana_address',
yourTurnkeyClient
);
console.log('Transaction result:', result);
} catch (error) {
console.error('Error performing transaction:', error);
}
}
Common Use Cases
1. Purchasing Multiple NFTs in One Transaction
const result = await legoClient.MagicEden.SwapForNFT({
items: [
{ address: '0x7E0b0363804C6C79AAb9aB51850bF8F3D561f868', tokenId: '42' },
{ address: '0x7E0b0363804C6C79AAb9aB51850bF8F3D561f868', tokenId: '43' }
],
token: {
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', // WBTC
decimals: 8,
chainId: 1
},
userAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
});
2. Purchasing with Different Tokens
// Using USDC
const usdcResult = await legoClient.MagicEden.SwapForNFT({
items: [{ address: '0x...', tokenId: '1' }],
token: {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
decimals: 6,
chainId: 1
},
userAddress: '0x...'
});
// Using DAI
const daiResult = await legoClient.MagicEden.SwapForNFT({
items: [{ address: '0x...', tokenId: '1' }],
token: {
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
decimals: 18,
chainId: 1
},
userAddress: '0x...'
});
3. Checking Token Balances
import { ShogunBalancesApiClient } from '@shogun-sdk/money-legos';
// Initialize the balances client
const balancesClient = new ShogunBalancesApiClient('YOUR_API_KEY');
// Check EVM wallet balances
const evmBalances = await balancesClient.getEvmWalletBalance('0x742d35Cc6634C0532925a3b844Bc454e4438f44e');
console.log('EVM Balances:', evmBalances);
// Check Solana token balances
const solanaBalances = await balancesClient.getSolanaTokenBalances('your_solana_address');
console.log('Solana Balances:', solanaBalances);
// Get token price in USD
const tokenPrice = await balancesClient.getTokenUSDPrice(
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
1 // Ethereum mainnet
);
console.log('Token Price:', tokenPrice);
// Get token information
const tokenInfo = await balancesClient.getTokenInfo(
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
1 // Ethereum mainnet
);
console.log('Token Info:', tokenInfo);
Error Handling
Here's a comprehensive example of error handling:
async function purchaseNFTWithErrorHandling() {
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 error cases
switch (result.error) {
case 'INSUFFICIENT_BALANCE':
console.error('Not enough tokens to complete the purchase');
break;
case 'INVALID_NFT':
console.error('The NFT you\'re trying to purchase doesn\'t exist');
break;
case 'PRICE_CHANGED':
console.error('The price has changed, please try again');
break;
default:
console.error(`Failed to fetch data: ${result.error}`);
}
return;
}
// Success case
console.log('Purchase data fetched successfully:', result.data);
// Execute transactions...
} catch (error) {
// Handle unexpected errors
if (error instanceof Error) {
console.error('Unexpected error:', error.message);
console.error('Stack trace:', error.stack);
} else {
console.error('Unknown error occurred:', error);
}
}
}
API Reference
LegoClient
Constructor
new LegoClient(config: LegoClientConfig)
Parameters:
config.apiKey
(string): Your API key for authenticationconfig.baseUrl
(string, optional): Custom API base URL
Methods
MagicEden.SwapForNFT(props: FetchLegoProps): Promise<LegoResult>
Fetches Lego data for purchasing NFTs.
Parameters:
props.items
(NFTItem[]): Array of NFT items to purchaseprops.token
(Token): Token to use for the purchaseprops.userAddress
(string): Address of the user making the purchase
Returns:
A promise that resolves to a LegoResult
object containing:
status
(boolean): Whether the request was successfulerror
(string): Error message if anydata
(ResponseData | null): The result data if successfulisLoading
(boolean): Whether the request is in progressrefetch
(function): Function to refetch the data with the same parameters
OneShotClient
Constructor
new OneShotClient(apiKey: string, baseUrl: string)
Methods
fetchQuote(params: QuoteParams): Promise<QuoteResponse>
Fetches a quote for a cross-chain swap.
Parameters:
params.srcChain
(number): Source chain IDparams.destChain
(number): Destination chain IDparams.srcToken
(string): Source token addressparams.destToken
(string): Destination token addressparams.amount
(string): Amount to swapparams.senderAddress
(string): Address of the senderparams.slippage
(number): Slippage tolerance in percentage
Returns:
A promise that resolves to a QuoteResponse
object containing:
status
(boolean): Whether the request was successfulerror
(string): Error message if anydata
(QuoteTypes | null): The quote data if successful
ShogunBalancesApiClient
Constructor
const balancesClient = new ShogunBalancesApiClient(apiKey: string)
Methods
getEvmWalletBalance(address: string): Promise<EvmBalances>
Gets the token balances for an EVM wallet.
Parameters:
address
(string): The wallet address
Returns:
A promise that resolves to an EvmBalances
object containing token balances.
getSolanaTokenBalances(address: string): Promise<SolanaBalances>
Gets the token balances for a Solana wallet.
Parameters:
address
(string): The wallet address
Returns:
A promise that resolves to a SolanaBalances
object containing token balances.
getTokenUSDPrice(tokenAddress: string, chainId: number): Promise<TokenPrice>
Gets the USD price for a token.
Parameters:
tokenAddress
(string): The token addresschainId
(number): The chain ID
Returns:
A promise that resolves to a TokenPrice
object containing the token price.
Types
interface NFTItem {
address: string; // NFT contract address
tokenId: string; // NFT token ID
}
interface Token {
address: string; // Token contract address
decimals: number; // Token decimals (e.g., 18 for ETH)
chainId: number; // Chain ID (e.g., 1 for Ethereum mainnet)
}
interface ResponseData {
steps: Step[]; // Array of transaction steps
details: Details; // Transaction details
fees: Fees; // Fee information
}
Best Practices
- Always Check Response Status
const result = await legoClient.MagicEden.SwapForNFT({...});
if (!result.status) {
// Handle error
return;
}
- Use TypeScript for Better Type Safety
import { LegoClient, NFTItem, Token } from '@shogun-sdk/money-legos';
const items: NFTItem[] = [...];
const token: Token = {...};
- Implement Proper Error Handling
try {
const result = await legoClient.MagicEden.SwapForNFT({...});
// Handle success
} catch (error) {
// Handle errors
}
- Keep API Keys Secure
// Use environment variables
const legoClient = new LegoClient({
apiKey: process.env.SHOGUN_API_KEY,
});
Troubleshooting
Common issues and their solutions:
API Key Issues
- Ensure your API key is valid and active
- Check if you've exceeded your API rate limits
- Verify the API key format
Transaction Failures
- Check if you have sufficient balance
- Verify gas settings
- Ensure you're on the correct network
Invalid NFT Data
- Verify the NFT contract address
- Check if the token ID exists
- Ensure the NFT is available for purchase
Support
If you encounter any issues or need help:
- Join our Discord Community)
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago