@prime-vaults/web3
v0.1.6
Published
Token SDK for Web3 DApps - Components, hooks, and utilities for token/chain management
Maintainers
Readme
@prime-vaults/web3
Web3 SDK for DApps - Components, hooks, and utilities for token/chain management.
Features
- Token components with auto-fetch from API
- Chain utilities with viem/chains integration
- Token balance & approval hooks
- Transaction mutation with React Query
- Format utilities for amounts, USD, numbers
- TypeScript first, zero styling (BYO styles)
Installation
pnpm add @prime-vaults/web3Peer Dependencies
pnpm add react wagmi viem @tanstack/react-queryQuick Start
import { TokenProvider } from '@prime-vaults/web3'
function App() {
return (
<TokenProvider apiUrl="https://api.example.com/api">
<YourApp />
</TokenProvider>
)
}Components
Token Components
import { TokenIcon, TokenSymbol, TokenName, TokenAmount, TokenBalance } from '@prime-vaults/web3'
// Token icon with auto-fetch
<TokenIcon address="0x..." chainId={80094} className="w-8 h-8" />
// Token symbol/name
<TokenSymbol address="0x..." chainId={80094} /> // "HONEY"
<TokenName address="0x..." chainId={80094} /> // "Honey Token"
// Token amount (raw bigint)
<TokenAmount address="0x..." chainId={80094} rawAmount={1000000000000000000n} />
// Token amount (decimalized)
<TokenAmount address="0x..." chainId={80094} amount={1.5} />
// As USD value
<TokenAmount address="0x..." chainId={80094} rawAmount={1000000000000000000n} usd />
// Token balance (connected wallet)
<TokenBalance address="0x..." chainId={80094} />
<TokenBalance address="0x..." chainId={80094} usd />Chain Components
import { ChainIcon, ChainName, ChainSymbol } from '@prime-vaults/web3'
<ChainIcon chainId={80094} className="w-6 h-6" />
<ChainName chainId={80094} /> // "Berachain"
<ChainSymbol chainId={80094} /> // "BERA"Hooks
Token Hooks
import {
useToken,
useTokensByChain,
useTokenAmount,
useTokenBalance,
} from '@prime-vaults/web3'
// Get single token (auto-fetches if not in store)
const token = useToken(80094, '0x...')
// { symbol, name, decimals, priceUSD, logoURI, ... }
// Get all tokens for a chain
const tokens = useTokensByChain(80094)
// Format token amount
const { formatted, usd, symbol } = useTokenAmount(80094, '0x...', rawAmount)
// Get token balance for connected wallet
const { balance, formatted, usd, isLoading } = useTokenBalance({
chainId: 80094,
address: '0x...',
})Approval Hook
import {
useApproveToken,
useAllowance,
useIsApproved,
} from '@prime-vaults/web3'
// Full approval flow
const { approve, isApproved, allowance, isLoading } = useApproveToken({
chainId: 80094,
tokenAddress: '0x...',
spenderAddress: '0x...',
amount: parseUnits('100', 18),
})
// Just check allowance
const { allowance, refetch } = useAllowance({
chainId: 80094,
tokenAddress: '0x...',
spenderAddress: '0x...',
})
// Just check if approved
const isApproved = useIsApproved({
chainId: 80094,
tokenAddress: '0x...',
spenderAddress: '0x...',
amount: parseUnits('100', 18),
})Transaction Mutation
Built on React Query's useMutation - returns standard mutation result.
import { useTxMutation } from '@prime-vaults/web3'
const tx = useTxMutation({
address: '0x...',
abi: vaultAbi,
functionName: 'deposit',
chainId: 80094, // optional, defaults to connected chain
confirmations: 1, // optional, default 1
callbacks: {
onSigning: () => toast.loading('Please sign in wallet...'),
onPending: (hash) => toast.loading(`Tx submitted: ${hash.slice(0, 10)}...`),
onSuccess: (hash, receipt) => toast.success('Transaction confirmed!'),
onError: (error, isUserRejection) => {
if (isUserRejection) toast.warn('Transaction cancelled')
else toast.error(error.message)
},
},
invalidateKeys: ['my-custom-query'], // optional, extra query keys to invalidate
})
// Execute transaction
await tx.mutateAsync({ args: [amount, receiver], value: parseEther('0.1') })
// Or without args (for no-param functions)
await tx.mutateAsync({})
// Returns UseMutationResult from React Query
tx.isPending // loading state
tx.isSuccess // success state
tx.isError // error state
tx.data // TransactionReceipt
tx.error // Error object
tx.mutate() // fire-and-forget version
tx.reset() // reset mutation stateAuto-invalidates queries: readContract, readContracts, balance, token, blockNumber, web3-sdk
Note: Returns standard UseMutationResult from React Query - all mutation properties available.
Utilities
Format Utilities
import {
formatTokenAmount,
formatNumber,
formatUsdValue,
formatPrice,
formatTimeAgo,
calculateUsdValue,
formatBps,
parseBps,
} from '@prime-vaults/web3'
// Format bigint to display string
formatTokenAmount(1000000000000000000n, 18) // "1.00"
formatTokenAmount(1234567890000000000000n, 18) // "1,234.57"
formatTokenAmount(10000000000000n, 18) // "0.00001"
formatTokenAmount(123n, 18) // "0.0₁₃123" (subscript)
// Format numbers (auto trim trailing zeros for small numbers)
formatNumber(1234567) // "1.23M"
formatNumber(1234.56) // "1,234.56"
formatNumber(12.3) // "12.30"
formatNumber(0.123) // "0.123"
formatNumber(0.001) // "0.001"
formatNumber(0.0000001) // "0.0₆1" (subscript for very small)
// Format USD
formatUsdValue(1234567) // "$1.23M"
formatUsdValue(1234.56) // "$1,234.56"
formatUsdValue(0.005) // "<$0.01"
// Format price (supports very small prices like meme coins)
formatPrice(1234.56) // "$1,234.56"
formatPrice(0.001) // "$0.001"
formatPrice(0.00000123) // "$0.0₅123" (subscript)
// Calculate USD value
calculateUsdValue(1000000000000000000n, 18, 2500) // 2500
// Format relative time
formatTimeAgo(new Date(Date.now() - 30000)) // "30s ago"
formatTimeAgo(new Date(Date.now() - 3600000)) // "1h ago"
formatTimeAgo(new Date(Date.now() - 86400000)) // "1d ago"
// Format basis points to percentage (1 bps = 0.01%)
formatBps(100n) // "1.00%"
formatBps(50n) // "0.50%"
formatBps(10000n) // "100.00%"
// Parse percentage to basis points
parseBps(1) // 100n (1% = 100 bps)
parseBps(0.5) // 50n (0.5% = 50 bps)Chain Utilities
import {
SUPPORTED_CHAINS,
SUPPORTED_VIEM_CHAINS,
VIEM_CHAINS,
getChainConfig,
getSupportedChainIds,
getExplorerAddressUrl,
getExplorerTxUrl,
getExplorerTokenUrl,
getChainLogoUrl,
getChainName,
getChainShortName,
getChainColor,
getNativeSymbol,
getViemChain,
getPublicClient,
} from '@prime-vaults/web3'
// Get chain config
const config = getChainConfig(80094)
// { chain, chainId, name, shortName, explorerUrl, nativeSymbol, logoUrl, color }
// Access raw viem Chain for wagmi config
const viemChains = SUPPORTED_VIEM_CHAINS // Chain[]
const berachain = VIEM_CHAINS.berachain // Chain
// Explorer URLs
getExplorerAddressUrl('0x...', 80094) // "https://berascan.com/address/0x..."
getExplorerTxUrl('0x...', 80094) // "https://berascan.com/tx/0x..."
getExplorerTokenUrl('0x...', 80094) // "https://berascan.com/token/0x..."
// Chain info
getChainName(80094) // "Berachain"
getChainShortName(80094) // "BERA"
getChainColor(80094) // "#FF6B00"
getNativeSymbol(80094) // "BERA"
getChainLogoUrl(80094) // "https://icons.llamao.fi/icons/chains/rsz_berachain.jpg"
getSupportedChainIds() // [1, 56, 42161, 1116, 80094]
// Get raw viem Chain object
const chain = getViemChain(80094)
// Create PublicClient for reading blockchain data
const client = getPublicClient(80094)
const balance = await client.getBalance({ address: '0x...' })Supported Chains
| Chain | ID | Short | Color | | --------- | ----- | ----- | ------- | | Ethereum | 1 | ETH | #627EEA | | BNB Chain | 56 | BNB | #F3BA2F | | Arbitrum | 42161 | ARB | #28A0F0 | | Core DAO | 1116 | CORE | #FF9500 | | Berachain | 80094 | BERA | #FF6B00 |
Address Utilities
import { isEqualAddress, normalizeAddress } from '@prime-vaults/web3'
// Compare addresses (case-insensitive, null-safe)
isEqualAddress('0xABC...', '0xabc...') // true
isEqualAddress(undefined, '0xABC...') // false
// Normalize address to lowercase
normalizeAddress('0xAbC...DeF') // '0xabc...def'
normalizeAddress('invalid') // undefinedTypes
import type {
Token,
TokenKey,
FormattedAmount,
ChainConfig,
TxCallbacks,
TxArgs,
UseTxMutationOptions,
} from '@prime-vaults/web3'
interface Token {
_id: string
chainId: number
address: `0x${string}`
symbol: string
name: string
decimals: number
priceUSD?: number
logoURI?: string
// ...
}
interface ChainConfig {
chain: Chain // raw viem Chain
chainId: number
name: string
shortName: string
explorerUrl: string
nativeSymbol: string
logoUrl?: string
color: string
}API Requirements
The SDK expects the following API endpoint:
GET /analytics/tokensResponse format:
{
"success": true,
"data": {
"tokens": [
{
"address": "0x...",
"chainId": 80094,
"symbol": "HONEY",
"name": "Honey",
"decimals": 18,
"logoURI": "https://...",
"priceUSD": 1.0
}
]
}
}Advanced Usage
Custom Query Invalidation
Use TX_MUTATION_KEY to invalidate your custom queries on transaction success:
import { TX_MUTATION_KEY } from '@prime-vaults/web3'
import { useQuery } from '@tanstack/react-query'
// Your custom query
const { data } = useQuery({
queryKey: [TX_MUTATION_KEY, 'my-data'],
queryFn: fetchMyData,
})
// Will be auto-invalidated after any useTxMutation successUsing viem Chains for Wagmi Config
import { SUPPORTED_VIEM_CHAINS } from '@prime-vaults/web3'
import { createConfig, http } from 'wagmi'
const config = createConfig({
chains: SUPPORTED_VIEM_CHAINS,
transports: {
[1]: http(),
[56]: http(),
[42161]: http(),
[1116]: http(),
[80094]: http(),
},
})Prime Auth
Wallet authentication with JWT for Prime platform. Tokens are stored per wallet address - switching wallets loads existing tokens without re-signing.
Setup
import { WagmiProvider } from 'wagmi'
import { PrimeAuthProvider } from '@prime-vaults/web3'
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<PrimeAuthProvider
apiUrl="https://api.prime.com"
autoReSign={true}
callbacks={{
onLoginSuccess: (user) =>
toast.success(`Welcome ${user.fullname}!`),
onTokenExpired: () => toast.warning('Session expired'),
onLogout: () => toast.info('Logged out'),
onAccountChanged: (address) => console.log('Switched to', address),
}}
>
<YourApp />
</PrimeAuthProvider>
</QueryClientProvider>
</WagmiProvider>
)
}Hooks
import {
usePrimeAuth,
usePrimeUser,
usePrimeToken,
useIsAuthenticated,
useAuthApi,
} from '@prime-vaults/web3'
// Main hook - state + actions
function LoginButton() {
const { login, logout, isAuthenticated, isAuthenticating } = usePrimeAuth()
if (isAuthenticated) {
return <button onClick={logout}>Logout</button>
}
return (
<button onClick={login} disabled={isAuthenticating}>
{isAuthenticating ? 'Signing...' : 'Login'}
</button>
)
}
// User hook
function UserProfile() {
const user = usePrimeUser()
const isAuth = useIsAuthenticated()
if (!isAuth) return <p>Not logged in</p>
return <p>Welcome, {user?.fullname}</p>
}
// useAuthApi - axios instance with auto Authorization header
function ApiCall() {
const api = useAuthApi() // Uses apiUrl from provider
// or: const api = useAuthApi('https://custom-api.com')
const fetchData = async () => {
// Authorization header automatically added
// 401 errors automatically clear token
const { data } = await api.get('/users/api/v1/data')
return data
}
}
// Manual token access (if needed)
function ManualApiCall() {
const jwtToken = usePrimeToken()
const fetchData = () => {
return axios.get('/api/data', {
headers: { Authorization: `Bearer ${jwtToken}` },
})
}
}Features
- Per-wallet token storage - Switch wallet A → B → A, token for A is loaded without re-signing
- Auto re-sign on expiry - 401 response triggers re-authentication (if autoReSign enabled)
- Auto logout on disconnect - Wallet disconnect keeps tokens persisted for later
- useAuthApi hook - Axios instance with Authorization header, auto-handles 401
- JWT persistence - Stored in localStorage per wallet address
Store Access
import {
usePrimeAuthStore,
useCurrentAccount,
useCurrentToken,
useCurrentUser,
} from '@prime-vaults/web3'
// Hook selectors
const account = useCurrentAccount() // { jwtToken, user } | null
const token = useCurrentToken() // string | null
const user = useCurrentUser() // PrimeUser | null
// Full store access
const {
accounts, // Record<address, { jwtToken, user }>
setAccount, // (address, token, user?) => void
getAccount, // (address) => AccountData | null
clearAccount, // (address) => void
clearAll, // () => void
} = usePrimeAuthStore()Types
import type {
PrimeUser,
PrimeAuthConfig,
PrimeAuthCallbacks,
} from '@prime-vaults/web3'
interface PrimeUser {
id: string
avatar: string
fullname: string
address: `0x${string}`
status: 'active' | 'inactive'
shared_key: string
created_at: Date
updated_at: Date
}
interface PrimeAuthCallbacks {
onLoginStart?: () => void
onLoginSuccess?: (user: PrimeUser) => void
onLoginError?: (error: Error) => void
onLogout?: () => void
onTokenExpired?: () => void
onAccountChanged?: (newAddress: `0x${string}`) => void
}License
MIT
