@shogun-sdk/one-shot v1.3.31
@shogun-sdk/one-shot
React hooks and components for the Shogun SDK. This package provides React components and hooks for building cross-chain swap interfaces with support for both EVM and Solana chains.
Table of Contents
- Features
- Installation
- Getting Started
- Core Concepts
- API Reference
- Examples
- Best Practices
- Troubleshooting
- Support
Features
- Cross-chain token swaps between EVM and Solana chains
- Real-time token balance tracking
- Automatic slippage calculation
- Fee validation and management
- Support for affiliate fees
- Dynamic quote fetching
- TypeScript support
- React Query integration for efficient data fetching
Installation
npm install @shogun-sdk/one-shot
# or
yarn add @shogun-sdk/one-shotGetting Started
Basic Setup
'use client';
import { useSwap } from 'your-store-library';
import { ShogunBalancesProvider, ShogunQuoteProvider, Token } from '@shogun-sdk/money-legos';
import { useMemo } from 'react';
export const ShogunProvider = ({ children }: { children: React.ReactNode }) => {
// Get swap state from your store
const {
inputAmount,
tokenIn,
tokenOut,
recipientAddress,
slippage,
setLatestSuggestedAutoSlippageValue,
setInputAmount,
dynamicSlippage // true/false, only for Solana transactions
} = useSwap();
// Your wallet addresses
const yourAddresses = {
evmAddress: 'your-evm-address', // starts from 0x...
solAddress: 'your-solana-address'
};
// Configure affiliate wallets
const affiliateWallets = useMemo(() => ({
solana: process.env.SOLANA_AFFILIATE_WALLET!,
evm: process.env.EVM_AFFILIATE_WALLET!
}), []);
// Configure API settings
const api = useMemo(() => ({
key: process.env.SHOGUN_API_KEY!,
url: process.env.SHOGUN_API_URL!
}), []);
return (
<ShogunBalancesProvider apiKey={process.env.CODEX_API_KEY!}>
<ShogunQuoteProvider
swap={{
tokenIn: tokenIn as Token,
tokenOut: tokenOut as Token,
setLatestSuggestedAutoSlippageValue,
inputAmount,
setInputAmount,
recipientAddress,
slippage,
dynamicSlippage,
}}
system={{
api,
systemFeePercent: 0.01,
userEVMAddress: yourAddresses.evmAddress,
userSolanaAddress: yourAddresses.solAddress,
affiliateWallets,
}}
>
{children}
</ShogunQuoteProvider>
</ShogunBalancesProvider>
);
};Core Concepts
Providers
The SDK provides two main providers:
- ShogunBalancesProvider: Manages token balances across chains
- ShogunQuoteProvider: Handles swap quotes and related operations
Hooks
The SDK provides several hooks for managing state and operations:
- useShogunQuote: Access quote information and operations
- useShogunBalances: Access balance information
- useTokenBalances: Get token balances for specific addresses
API Reference
Types
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)
symbol?: string; // Token symbol (optional)
}
interface SwapConfig {
tokenIn: Token; // Source token
tokenOut: Token; // Destination token
setLatestSuggestedAutoSlippageValue: (value: number) => void;
inputAmount: string;
setInputAmount: (amount: string) => void;
recipientAddress: string;
slippage: number;
dynamicSlippage?: boolean; // Optional: Enable dynamic slippage for Solana
}
interface SystemConfig {
api: {
key: string;
url: string;
};
systemFeePercent: number;
userEVMAddress: string;
userSolanaAddress: string;
affiliateWallets: {
solana: string;
evm: string;
};
notifyAboutError?: (error: Error) => void; // Optional: Error notification callback
}
interface QuoteContextValue {
quotes: QuoteTypes | undefined;
errors: {
feeValidationError: string | null;
balanceError: string | null;
};
quoteRefetch: () => any;
fees: ICollectedFees | undefined;
isMaxBtnClicked: boolean;
handleMaxBalanceInput: (quote: QuoteTypes | undefined, fees: ICollectedFees | undefined, maxBtnClicked: boolean) => void;
setIsMaxBtnClicked: (isMaxBtnClicked: boolean) => void;
inputValue: string;
setInputValue: (inputValue: string) => void;
needRecalculateMaxValue: boolean;
isLoading: boolean;
isRefetching: boolean;
userInputAddress: string;
userOutputAddress: string;
}Examples
Basic Usage
import { useShogunQuote } from '@shogun-sdk/one-shot';
function SwapComponent() {
const { quote, isLoading, error } = useShogunQuote();
if (isLoading) {
return <div>Loading quote...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
return (
<div>
<h2>Swap Details</h2>
<pre>{JSON.stringify(quote, null, 2)}</pre>
</div>
);
}Wagmi v0 Integration
Basic Wallet Connection
import { useAccount, useConnect, useDisconnect } from 'wagmi';
import { InjectedConnector } from 'wagmi/connectors/injected';
import { useShogunQuote } from '@shogun-sdk/one-shot';
function WalletConnect() {
const { address, isConnected } = useAccount();
const { connect } = useConnect({
connector: new InjectedConnector(),
});
const { disconnect } = useDisconnect();
return (
<div>
{isConnected ? (
<div>
<p>Connected to {address}</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
) : (
<button onClick={() => connect()}>Connect Wallet</button>
)}
</div>
);
}Complete Swap Interface
import { useAccount, useConnect, useDisconnect, useNetwork, useSwitchNetwork } from 'wagmi';
import { InjectedConnector } from 'wagmi/connectors/injected';
import { useShogunQuote, useShogunBalances } from '@shogun-sdk/one-shot';
import { useState, useEffect } from 'react';
function SwapInterface() {
const { address, isConnected } = useAccount();
const { connect } = useConnect({
connector: new InjectedConnector(),
});
const { disconnect } = useDisconnect();
const { chain } = useNetwork();
const { switchNetwork } = useSwitchNetwork();
const [amount, setAmount] = useState('1000000000000000000'); // 1 ETH
const [selectedTokenIn, setSelectedTokenIn] = useState({
address: '0xEthereum...',
decimals: 18,
chainId: 1
});
const [selectedTokenOut, setSelectedTokenOut] = useState({
address: '0xArbitrum...',
decimals: 18,
chainId: 42161
});
// Get quote with connected wallet
const {
quote,
isLoading: quoteLoading,
error: quoteError,
fees,
handleMaxBalanceInput
} = useShogunQuote();
// Get balances for connected wallet
const {
evmBalances,
solanaBalances,
isLoadingEVM,
isLoadingSolana
} = useTokenBalances({
userEVMAddress: address || '',
userSolanaAddress: '', // Add your Solana address if needed
tokenIn: selectedTokenIn,
tokenOut: selectedTokenOut
});
// Handle max button click
const handleMaxClick = () => {
handleMaxBalanceInput(quote, fees, true);
};
if (!isConnected) {
return (
<div className="wallet-connect">
<h2>Connect Your Wallet</h2>
<button onClick={() => connect()}>Connect Wallet</button>
</div>
);
}
return (
<div className="swap-interface">
<div className="wallet-info">
<p>Connected: {address}</p>
<p>Network: {chain?.name}</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
<div className="token-selection">
<div className="input-token">
<h3>From</h3>
<select
value={selectedTokenIn.address}
onChange={(e) => setSelectedTokenIn({
...selectedTokenIn,
address: e.target.value
})}
>
<option value="0xEthereum...">ETH</option>
<option value="0xUSDC...">USDC</option>
</select>
<div className="amount-input">
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
min="0"
step="0.000000000000000001"
/>
<button onClick={handleMaxClick}>MAX</button>
</div>
<p>Balance: {evmBalances?.[selectedTokenIn.address] || '0'}</p>
</div>
<div className="output-token">
<h3>To</h3>
<select
value={selectedTokenOut.address}
onChange={(e) => setSelectedTokenOut({
...selectedTokenOut,
address: e.target.value
})}
>
<option value="0xArbitrum...">USDC on Arbitrum</option>
<option value="0xOptimism...">USDC on Optimism</option>
</select>
<p>Estimated Output: {quote?.outputAmount || '0'}</p>
<p>Balance: {evmBalances?.[selectedTokenOut.address] || '0'}</p>
</div>
</div>
{quoteLoading ? (
<div>Loading quote...</div>
) : quoteError ? (
<div>Error: {quoteError.message}</div>
) : (
<div className="swap-details">
<button
onClick={() => {/* Handle swap execution */}}
disabled={!quote || quoteLoading}
>
Swap
</button>
</div>
)}
</div>
);
}Available Hooks
useShogunQuote from ShogunQuoteContext
const {
quote,
isLoading,
error,
fees,
isMaxBtnClicked,
handleMaxBalanceInput,
setIsMaxBtnClicked,
inputValue,
setInputValue,
needRecalculateMaxValue,
userInputAddress,
userOutputAddress
} = useShogunQuote();useShogunBalances from ShogunBalancesProvider
const client = useShogunBalances();useTokenBalances
const {
evmBalances,
solanaBalances,
isLoadingEVM,
isLoadingSolana
} = useTokenBalances({
userEVMAddress: string,
userSolanaAddress: string,
tokenIn?: Token,
tokenOut?: Token
});Components
ShogunBalancesProvider
Provider for managing token balances across chains.
<ShogunBalancesProvider apiKey="your-api-key">
<YourApp />
</ShogunBalancesProvider>Props:
apiKey: string - Your CODEX API keychildren: React.ReactNode
ShogunQuoteProvider
Provider for managing swap quotes.
<ShogunQuoteProvider
swap={{
tokenIn: { address: '0x...', decimals: 18, chainId: 1 },
tokenOut: { address: '0x...', decimals: 18, chainId: 8453 },
setLatestSuggestedAutoSlippageValue: (value) => {},
inputAmount: '1000000000000000000',
setInputAmount: (amount) => {},
recipientAddress: '0x...',
slippage: 0.5,
dynamicSlippage: false,
}}
system={{
api: { key: '...', url: '...' },
systemFeePercent: 0.01,
userEVMAddress: '0x...',
userSolanaAddress: '...',
affiliateWallets: { solana: '...', evm: '0x...' },
}}
>
<YourApp />
</ShogunQuoteProvider>Props:
swap: SwapConfigsystem: SystemConfigchildren: React.ReactNode
Complete Example
Here's a complete example of a swap interface:
import { useShogunQuote, useShogunBalances } from '@shogun-sdk/one-shot';
import { useState } from 'react';
function SwapInterface() {
const [amount, setAmount] = useState('1000000000000000000'); // 1 ETH
// Get quote
const { quote, isLoading: quoteLoading, error: quoteError } = useShogunQuote();
// Get balances
const { balances, isLoading: balancesLoading, error: balancesError } = useShogunBalances();
if (quoteLoading || balancesLoading) {
return <div>Loading...</div>;
}
if (quoteError) {
return <div>Error fetching quote: {quoteError.message}</div>;
}
if (balancesError) {
return <div>Error fetching balances: {balancesError.message}</div>;
}
return (
<div className="swap-interface">
<h2>Cross-chain Swap</h2>
{/* Amount Input */}
<div className="input-group">
<label>Amount (ETH)</label>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
min="0"
step="0.000000000000000001"
/>
</div>
{/* Swap Button */}
<button
onClick={() => {/* Handle swap */}}
disabled={!quote || quoteLoading}
>
{quoteLoading ? 'Loading...' : 'Swap'}
</button>
</div>
);
}Types
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)
symbol?: string; // Token symbol (optional)
}
interface SwapConfig {
tokenIn: Token; // Source token
tokenOut: Token; // Destination token
setLatestSuggestedAutoSlippageValue: (value: number) => void;
inputAmount: string;
setInputAmount: (amount: string) => void;
recipientAddress: string;
slippage: number;
dynamicSlippage?: boolean; // Optional: Enable dynamic slippage for Solana
}
interface SystemConfig {
api: {
key: string;
url: string;
};
systemFeePercent: number;
userEVMAddress: string;
userSolanaAddress: string;
affiliateWallets: {
solana: string;
evm: string;
};
notifyAboutError?: (error: Error) => void; // Optional: Error notification callback
}
interface QuoteContextValue {
quotes: QuoteTypes | undefined;
errors: {
feeValidationError: string | null;
balanceError: string | null;
};
quoteRefetch: () => any;
fees: ICollectedFees | undefined;
isMaxBtnClicked: boolean;
handleMaxBalanceInput: (quote: QuoteTypes | undefined, fees: ICollectedFees | undefined, maxBtnClicked: boolean) => void;
setIsMaxBtnClicked: (isMaxBtnClicked: boolean) => void;
inputValue: string;
setInputValue: (inputValue: string) => void;
needRecalculateMaxValue: boolean;
isLoading: boolean;
isRefetching: boolean;
userInputAddress: string;
userOutputAddress: string;
}Best Practices
Provider Setup
- Always wrap your app with both providers in the correct order
- Use environment variables for sensitive data
- Memoize configuration objects to prevent unnecessary re-renders
Error Handling
- Implement proper error boundaries
- Use the provided error states from hooks
- Implement the
notifyAboutErrorcallback for error tracking
Performance Optimization
- Use
useMemofor expensive calculations - Implement proper loading states
- Use the provided refetch functions when needed
- Use
Type Safety
- Use TypeScript for better type checking
- Properly type your token configurations
- Use the provided interfaces for type safety
State Management
- Keep provider state minimal
- Use local state for UI-specific data
- Implement proper cleanup in useEffect hooks
Troubleshooting
Common issues and their solutions:
Provider Issues
- Ensure all required providers are present
- Check provider order (BalancesProvider should wrap QuoteProvider)
- Verify all required props are provided
- Check environment variables are properly set
Hook Usage Issues
- Make sure hooks are used within provider context
- Check hook parameters are correct
- Verify all required dependencies are installed
- Check for proper error handling
Type Errors
- Ensure proper TypeScript types are imported
- Check interface implementations
- Verify type definitions match your data
- Use proper type assertions when needed
Balance Issues
- Verify wallet addresses are correct
- Check chain IDs match your tokens
- Ensure proper RPC endpoints are available
- Check token decimals are correct
Quote Issues
- Verify token addresses are correct
- Check slippage settings
- Ensure proper fee configuration
- Verify affiliate wallet addresses
Support
If you encounter any issues or need help:
- Join our Discord Community
8 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
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
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago