@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-shot
Getting 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
notifyAboutError
callback for error tracking
Performance Optimization
- Use
useMemo
for 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
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
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