Only this pageAll pages
Powered by GitBook
1 of 65

Version 2

DISCOVER VECHAIN KIT

Loading...

Loading...

Loading...

Quickstart

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Migrations

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Hooks

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Customization

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Components

Loading...

Loading...

Loading...

Troubleshooting

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Fee Delegation

Loading...

Loading...

Social Login

Loading...

Loading...

Loading...

What's VeChain Kit?

VeChain Kit is an all-in-one SDK for building frontend applications on VeChain, supporting wallet integration, developer hooks, pre-built UI components, and more.

It offers:

  • Seamless Wallet Integration: Support for VeWorld, WalletConnect, and social logins.

  • Developer-Friendly Hooks: Easy-to-use React Hooks that let you read and write data on the VeChainThor blockchain.

  • Token Operations: Send and swap tokens, check balances, manage VET domains, and more—all in one place.

  • Pre-Built UI Components: Ready-to-use components (e.g., TransactionModal) to simplify wallet operations and enhance your users’ experience.

Cover

Live Demo

Cover

NPM Package

Cover

Getting Started

Cover

Customization

Cover

Components

Cover

Troubleshooting

What's new?

This update brings a refreshed interface, better performance, more flexibility, and an improved developer experience. It also transitions from Connex to the SDK, with V1 now deprecated.

⚡ Faster Development

  • We’ve introduced several optimizations that drastically reduce bundle size and speed up development builds.

  • To support modular workflows, we’ve also released standalone packages like and , which you can use independently.

  • More improvements are coming soon.

🎨 More Customization

You now have much more control over the vechain-kit modal:

  • Open specific flows in isolation (e.g., only Receive or Send, without exposing the rest of the modal).

  • Customize colors and fonts to match your brand.

  • Use Bottomsheets instead of Modals on mobile!

Check the section for all available options.

✨ Redesigned UI

In version 2, we have completely overhauled the user interface to simplify navigation and enhance user experience. The new design focuses on clarity and usability, placing a stronger emphasis on wallet features to streamline user interactions. This redesign aims to provide an intuitive and efficient workflow, allowing users to access essential functionalities effortlessly.

🔑 Quick Wallet Switching

Easily switch between wallets without the need to log out and log back in, enhancing the fluidity of your transactions and interactions.

🔄 Built-In Token Swap

Swap tokens directly inside the kit—no need to send users to external websites.

Powered by BetterSwap and VeTrade, swaps are now:

  • Seamless

  • Secure

  • Efficient

Users can manage assets more conveniently than ever.

🆓 Smarter Fee Delegation

No more configuring delegation services manually.

V2 includes:

  • Automatic transaction sponsorship for social-login users (via Generic Delegator)

  • An improved useSendTransaction() hook that lets you sponsor specific transactions selectively

Even if you’re not required to cover fees, you might still sponsor some transactions—for example, onboarding new users or showing fee costs to social-login accounts. Head over to the section to learn more about this.

🛠️ Easier Installation

A cleaner, more streamlined setup minimizes the time spent troubleshooting.

👋 Goodbye Connex, Hello SDK

Connex is deprecated. V2 now uses the SDK, offering more developer capabilities and a more modern foundation.

📱 Better VeWorld Mobile Integration

We now use the new VeWorld endpoints, delivering:

  • Smoother logins

  • Easier wallet switching

📘 Improved Documentation

Clearer, more complete documentation helps you transition and integrate features with confidence.

🚀 A Big Roadmap Ahead

Coming soon (and exclusive to V2):

  • Revamped login flow

  • Better cross-app connections

  • Transaction history

  • NFTs

Breaking Changes

V2 may have some breaking changes based on your V1 integration. Please read more in details what changed from V1 and how to migrate to version 2 in .

Version 1 Deprecation Support for version 1 will cease, and updates will target version 2, encouraging users to migrate to leverage new features.

Migrate Social Login Users

If you have an app with some custom login (eg: login with email, login with google, etc.) and you want to use this kit and migrate your users you will need to:

  • Create your Privy app

  • Manually add your users or use Privy's APIs

Privy/YourApp/Users/All Users/Add

Please refer to this documentation to import users through Privy APIs: https://docs.privy.io/reference/sdk/server-auth/classes/PrivyClient#importuser

3) Toggle the "Pregenerate EVM Wallet" so an embedded wallet will be created and associated to the user.

4) If your users where logging in with social you may need to ask them to associate that social login method after they login first time with their email.

Breaking Changes Overview

This provides a high-level summary of all breaking changes in VeChain Kit v2. For detailed information on each topic, please refer to the specific guides linked below.

Major Breaking Changes

1. Connex Removal

Migration Issues

Common problems when upgrading to VeChain Kit from other solutions or managing dependencies

Quick Fixes

  • Clean install: Delete node_modules and reinstall packages

  • useConnex is completely removed and replaced with useThor

  • This affects all blockchain interactions throughout your application

  • See API Migration Guide →

  • 2. New Contract Interaction Patterns

    • Introduction of useCallClause for reading contract data

    • New executeMultipleClausesCall for batch operations

    • Complete rewrite of transaction patterns

    • See API Migration Guide →

    3. Updated Transaction Building

    • New useBuildTransaction hook with improved type safety

    • Better error handling and transaction status tracking

    • See API Migration Guide →

    4. Module Removal

    • Entire VeBetterDAO module removed

    • Several utility modules deprecated

    • See Removed Features →

    5. Hook Restructuring

    • Many hooks moved to new locations for better organization

    • Import paths have changed significantly

    • See Hook Relocations →

    Quick Decision Guide

    Next Steps

    1. Read the complete removal list to check if you use any removed features

    2. Review the API migration patterns for code examples

    3. Follow the migration checklist step by step

    Getting Help

    • GitHub Issues: Report issues

    • Documentation: VeChain Kit v2 Docs

    • Community: Discord

    Check versions: Ensure compatible peer dependency versions
  • Complete removal: Remove all old DApp Kit packages before installing VeChain Kit

  • Issues in This Section

    Peer Dependencies

    Resolving version conflicts and dependency mismatches when installing VeChain Kit.

    From DApp Kit

    Complete migration guide for projects upgrading from DApp Kit, including package removal and import updates.


    Can't find your issue? Search our GitHub Issues or ask on Discord.

    DeFi integrations
  • And more

  • @vechain/vechain-contract-types
    @vechain/contract-getters
    Customization
    Fee Delegation
    the following section

    Fonts

    Customize fonts used throughout VeChain Kit components:

    fonts: {
        family: 'Inter, sans-serif', // Font family (e.g., "Inter, sans-serif", "'Roboto', sans-serif")
        sizes: {
            small: '12px', // Font size for small text
            medium: '14px', // Font size for medium text
            large: '16px', // Font size for large text
        },
        weights: {
            normal: 400, // Normal font weight
            medium: 500, // Medium font weight
            bold: 700, // Bold font weight
        },
    }

    Important: Font customization only affects VeChain Kit components (modals, buttons, etc.) and does not leak to your host application. Fonts are scoped to VeChain Kit containers only.

    You can customize any subset of font properties - unspecified values will use defaults:

    // Only customize font family
    fonts: {
        family: 'Inter, sans-serif',
    }
    
    // Only customize font sizes
    fonts: {
        sizes: {
            medium: '15px',
            large: '18px',
        },
    }

    Oracle

    useGetTokenUsdPrice

    A React hook that fetches the current USD price for supported tokens (B3TR, VET, VTHO) from the VeChain oracle contract.

    Usage

    Parameters

    Fetch the price for the following token symbols: 'B3TR', 'VET', 'VTHO'.

    Returns

    The hook returns a TanStack Query result object with the following properties:

    Property
    Type
    Description

    Implementation Details

    The hook internally:

    • Uses the VeChain oracle contract to fetch real-time price data

    • Automatically handles network configuration (mainnet/testnet)

    • Caches results using TanStack Query

    • Only fetches when both Thor connection and network type are available

    Example with Multiple Tokens

    Migrate from DAppKit

    1) Install and overwrite the Provider.

    2) Replace all imports of @vechain/dapp-kit-react @vechain/dapp-kit and @vechain/dapp-kit-ui with @vechain/vechain-kit.

    3) Wherever you use the account property from the useWallet() hook you need to access the user address differently:

    // dapp-kit
    const { account } = useWallet()
    console.log(account) // 0x000000dsadsa
    
    // vechain-kit
    const { account } = useWallet()
    console.log(account.address, account.domain, account.image)

    4) Use the useSendTransaction() hook from @vechain/vechain-kit to send your transactions to the network. Read how to use the hook .

    6) If you use useConnex() by importing from dapp-kit, import it from vechain-kit.

    7) If you are using certificate signing to authenticate your users with your backend to issue a jwt/session token you will need to switch to use signTypedData instead, since Privy and Smart Accounts does not support the VeChain certificate authentication signature type. Read how to do .

    8) Double-check your yarn.lock to see that all the @vechain/dapp-kit-react @vechain/dapp-kit and @vechain/dapp-kit-ui installs are using the the 1.5.0 version.

    Remove all installations of @vechain/dapp-kit @vechain/dapp-kit-ui and @vechain/dapp-kit-react from your app. If you need some specific hooks or methods from dapp-kit you can import them directly from the @vechain/vechain-kit.

    Should I use it?

    VeChain provides a robust set of technologies to facilitate app development, including the VeChain Kit, DApp Kit, and SDKs. This section will guide you through these resources, helping you make informed decisions to choose the most suitable tool for your application's needs.

    VeChain SDK

    • Similar to viem

    • Optimized for backend development and scripting tasks.

    • Establish wallet connections from scratch for full control and customization of transactions.

    The SDK is ideal for developers seeking to harness the power of VeChainThor in their backend architecture or script-based solutions.

    • The DApp Kit is designed to be a lightweight library, handling only essential blockchain features.

    • Only allows connection with VeWorld, Wallet Connect and Sync2

    • Does not provide hooks for sending transactions, SDK must be used for that

    • Supports multiple framworks (React, Next, Svelte, Vue, Angular)

    DApp Kit is ideal to who wants a lightweight package, that handles only the essential.

    VeChain Kit

    • It uses both dappkit and sdk under the hood, so you have all the functionalities of above

    • You have social login

    • You have out of the box components, hooks, and functionalities

    • Only supports Next and React frameworks

    Which one to use?

    Do you want social login?

    If yes, you need to go with VeChain Kit, or build your social login implementation on your own.

    Do you want to enhance your app with the VeChaikit UI components?

    If you want to allow your users to swap, send tokens, view balances, switch account out of the box then you need to use VeChain Kit.

    Do you want out of the box hooks for transactions, signings, avatars, etc.?

    Then you can use the VeChain Kit, it has all the hooks you may need, and more will be added.

    Peer Dependencies

    Ensure that your project's peer dependencies align with VeChain Kit's specifications. Mismatched versions can cause installation failures, runtime errors, or unexpected behaviour.

    Common Issues

    • Package installation fails with peer dependency warnings

    • Runtime errors about missing dependencies

    • Version conflicts between VeChain Kit and your existing packages

    Solution

    1. Clean Installation

    Often resolves dependency caching issues:

    1. Check Required Peer Dependencies

    VeChain Kit requires specific versions of:

    • Chakra UI React: ^2.8.2

    • TanStack React Query: ^5.64.2

    • VeChain DApp Kit React: 2.0.1

    1. Update or Downgrade Packages

    You may need to adjust package versions to maintain compatibility:

    Blockchain Hooks

    useCurrentBlock()

    Fetches the current block from the blockchain with automatic updates.

    Features:

    • Auto-refreshes every 10 seconds

    • Caches data for 1 minute

    • Returns the latest expanded block information

    Example:

    useTxReceipt()

    Polls the blockchain for a transaction receipt until it is found or times out.

    Parameters:

    • txId: (string) The transaction ID to monitor

    • blockTimeout: (optional number) Number of blocks to wait before timing out (default: 5)

    Returns:

    • data: Transaction receipt (TransactionReceipt)

    • isLoading: Boolean indicating if the receipt is being fetched

    • error: Error object if the operation fails

    Example:

    Utility Functions

    useEvents()

    Fetches events from the blockchain based on specified criteria.

    Glass Effect

    Enable and configure glass morphism effects:

    effects: {
        glass: {
            enabled: true, // Enable glass effects
            intensity: 'low' | 'medium' | 'high', // Glass intensity
        },
        backdropFilter: {
            modal: 'blur(15px)', // Optional: override modal blur
            // overlay blur is set via overlay.blur
        },
    }

    When glass is enabled, the system automatically:

    • Applies appropriate blur values based on intensity

    • Adjusts background opacities for glass morphism effect

    • Maintains readability across all surfaces

    Glass Intensity Settings:

    • low: blur(2px), modal opacity 0.6, sticky header opacity 0.7

    • medium: blur(3px), modal opacity 0.7, sticky header opacity 0.8

    • high: blur(5px), modal opacity 0.8, sticky header opacity 0.85

    When glass effects are enabled:

    • Background colors automatically get reduced opacity based on intensity

    • Blur values are applied to modal, overlay, and sticky header

    • The system ensures readability while maintaining the glass aesthetic

    If glass is disabled, default blur values are still applied (not removed).

    Styling Issues

    VeChain Kit uses Chakra UI internally, which can cause styling conflicts with other CSS frameworks and custom styles.

    Common Problems

    • CSS framework styles being overridden by VeChain Kit

    • VeChain Kit components not rendering correctly due to missing Chakra setup

    • Theme conflicts when your app also uses Chakra UI

    • Unexpected styling on images, buttons, or form elements

    Quick Fixes

    • Install Chakra UI as a peer dependency even if you don't use it directly

    • Use CSS layers to control style precedence

    • Wrap your app in ChakraProvider with ColorModeScript

    Issues in This Section

    Setting up Chakra UI properly and resolving conflicts when your app also uses Chakra.

    Using CSS layers to resolve conflicts with Tailwind, Bootstrap, and other CSS frameworks.


    Can't find your issue? Search our or ask on .

    Bottomsheet

    On mobile devices, using bottom sheets instead of modals can enhance the user experience by providing a more intuitive and unobtrusive way to present information. Bottom sheets slide up from the bottom of the screen, allowing users to continue interacting with the rest of the app while accessing additional content or actions.

    To enable this feature, simply set the property useBottomSheetOnMobile to true, offering a seamless transition that maintains user engagement and minimizes disruptions in the app’s interface.

    <VeChainKitProvider
        theme={{
            modal: {
                useBottomSheetOnMobile: true
            },
        }}
    >
        {children}
    </VeChainKitProvider>
    

    Setup Legal Documents (optional)

    To prompt users to review and accept your policies, like Terms and Conditions, Privacy Policy, or Cookie Policy, VeChainKit offers a simple plug-and-play solution.

    You can avoid building your own if you haven't already.

    By enabling also the tracking consent, you will allow VeChainKit to prompt your users to collect data to improve the kit.


    When the legalDocuments option is configured, the users will see:

    • Left: A modal prompt when connecting their wallet, requiring them to review and accept required and optional legal documents.

    Setup Privy (optional)

    If you have your own Privy app, you can pass an additional prop with your settings.

    Go to and create an app. You will find the APP ID and the CLIENT ID in the App Settings tab.

    For further information on how to implement Privy SDK please refer to their docs:

    This project uses:

    • for Privy connection type

    • for ecosystem cross app connection

    Installation

    How to install and set up VeChain Kit in your project

    Get the template

    Only one command to setup

    Or you can install the kit in your package as follows.

    Quick-start

    Smart Accounts v1 to v3

    Upgrading to unlocks multi-clause support, enhanced security, and other essential features for transactions on VeChain. This feature is available in VeChain Kit from .

    When integrating social login in your app, users might have a version 1 smart account. This version doesn’t support multiclause transactions, potentially causing issues within your app.

    To address this scenario, consider wrapping your onClick handler or using another suitable method to prompt users to upgrade their smart accounts to version 3.

    The kit makes available both the hooks to know if the upgrade is required and the component for upgrading:

    • useUpgradeRequired(smartAccountAddress, ownerAddress, targetVersion: 3) is the hook that will let you know if the user is on v1 and needs to upgrade to v3

    Intro

    Component Overview

    All views of the kit (Receive, Send tokens, Swap, Profile, Settings, Notifications, Ecosystem, etc.) can be opened isolated by the other parts of the app, so you could add your own custom receive button that on click will open the Receive modal of the kit.

    The hook offers a versatile suite of components for seamless integration into web applications. Below are some of the key components:

    • Wallet Button: Dynamically triggers either a login or account modal based on the user's connection status.

    Connection Types

    VeChain Kit supports 3 types of connections:

    1) Privy

    This connection type is often used by organizations like VeBetterDAO, Cleanify, and Greencart. When connected, users can back up their embedded wallets, sign transactions without confirmation prompts, and add login methods. By connecting with Privy, developers use their personal APP_ID and CLIENT_ID to create their own app on Privy.

    From DApp Kit

    If you're coming from DApp Kit or SDK, you may have version conflicts from different versions installed across your project.

    VeChain Kit includes DApp Kit functionality, but still requires @vechain/dapp-kit-react as a peer dependency for compatibility.

    Problem

    Multiple versions of DApp Kit packages can cause:

    Dev support

    Are you having issues using the kit? Join our discord server to receive support from our devs or open an issue on our Github!

    Check our .

    Contact us on Discord:

    Open an issue on Github:

    Chakra Conflicts

    VeChain Kit components are wrapped in their own Chakra Provider (v2), ensuring consistent styling throughout the modals. This can cause conflicts if your app also uses Chakra UI.

    Problem

    • VeChain Kit components not rendering correctly

    Fee Delegation

    VeChain Kit includes built-in fee delegation handling. If you already have fee delegation in your app, you must remove it to avoid conflicts.

    Problem

    Using both your own fee delegation and VeChain Kit's delegation causes:

    • Transaction failures

    Intro

    The kit provides hooks for developers to interact with smart contracts like VeBetterDAO, VePassport, veDelegate, and price oracles.

    The hooks in this package provide a standardized way to interact with various blockchain and web services. All hooks are built using (formerly React Query), which provides powerful data-fetching and caching capabilities.

    Common Features

    Every hook in the @api directory returns a consistent interface that includes:

    Generic Delegator - Default Option

    Handling Transaction Fee Notifications

    When using Vechain Kit with the Generic Delegator, it's important to provide clear information to users regarding transaction fees. Consider the following UI alerts:

    • Transaction Confirmation Alert: Notify users of the exact amount of VET, VTHO, or B3TR tokens that will be deducted from their wallet when initiating a transaction.

    import { useGetTokenUsdPrice } from '@vechain/vechain-kit';
    
    function TokenPrice() {
      const { data: vetPrice, isLoading, error } = useGetTokenUsdPrice('VET');
      
      if (isLoading) return <div>Loading...</div>;
      if (error) return <div>Error fetching price</div>;
      
      return <div>VET Price: ${vetPrice}</div>;
    }
    const { data, isLoading, error } = useCurrentBlock();

    Insufficient Funds Alert: Alert users if they lack sufficient balance to cover the transaction fees, providing details on the required amount.

    Implement these alerts effectively to enhance user experience and ensure transparency regarding transaction costs.

    By using this default option, you, as an app owner, won't spend any money for the user operations.

    Estimating Transaction Costs

    You can use the useEstimateGas hook to determine how much a user will spend on transaction fees. This hook provides an estimation of the gas fees required for a transaction before it is initiated. Additionally, users have the option to change the default token used for transaction fees in the settings, allowing for greater flexibility and control over transaction costs.

    The useGasEstimation hook is designed for estimating transaction gas costs within applications using the Generic Delegator fee sponsorship system. It handles multi-token estimations, automatically selecting the most suitable gas token based on user balance and token availability. The hook is suited for scenarios requiring flexibility and balance validation in transaction processes.

    Sponsor only specific transactions

    To implement this feature, use the useSendTransaction hook with the parameters sponsor: true or sponsor: false based on the specific transactions you wish to sponsor. By including the feeDelegationUrl, you can direct the fee delegation process accordingly, giving you control over which transactions are sponsored. This offers flexibility in transaction sponsorship while maintaining an efficient fee management strategy.

    here
    here
    DApp Kit
    Chakra Conflicts
    CSS Framework Conflicts
    GitHub Issues
    Discord
    Use yarn or npm to build the packages. pnpm is known to have some issues we are trying to solve.

    Quick Links

    If your issue is not addressed here, please reach out to us:

    • Discord: https://discord.com/invite/vechain

    • GitHub Issues: https://github.com/vechain/vechain-kit/issues

    Common Issue Categories

    🔄 Migration Issues

    Problems when upgrading from DApp Kit or managing dependencies

    • Peer Dependencies

    • From DApp Kit

    🎨 Styling Issues

    CSS conflicts and theming problems

    • Chakra UI Conflicts

    • CSS Framework Conflicts

    ⚙️ Integration Issues

    Runtime and functionality problems

    • Fee Delegation

    • Privy Popup Blocking

    Before You Start

    1. Check that you're using the latest version of VeChain Kit

    2. Ensure all peer dependencies are correctly installed

    3. Clear your build cache (rm -rf node_modules .next && npm install)


    Can't find your issue? Search our GitHub Issues or ask on Discord.

    Troubleshooting section
    https://discord.gg/wGkQnPpRVq
    https://github.com/vechain/vechain-kit/issues

    data

    number

    The current token price in USD.

    isLoading

    boolean

    Indicates if the query is currently loading.

    error

    Error | null

    Describes any error that occurred during query.

    isError

    boolean

    Indicates if there was an error in the query.

    Transaction Components: A set of components that will guide the user through the transaction lifecycle (confirm -> loading -> success/error). You can use both a modal or a toast.

  • DAppKitWalletButton: Provides a focused interface for selecting wallet connection options, in case you do not want social login.

  • These components collectively enhance user interaction and streamline.

    Head over the VeChain Kit homepage to see all the components in action.

    const { open: openProfileModal } = useProfileModal();
    
    // Open profile modal in isolated mode
    openProfileModal({ isolatedView: true });
    You can import privy from the kit as

    If you setup your own Privy be sure to go over the recommended security settings provided by Privy: https://docs.privy.io/guide/security/implementation/ and https://docs.privy.io/guide/security/implementation/csp

    Pros of self hosting Privy:

    • No UI confirmations on users transactions

    • Allow your users to backup their keys and update security settings directly in your app

    • Targetted social login methods

    Cons:

    • Price

    • Responsibilities to correctly secure your Privy account, since it contains access to user's wallet settings

    • Your users will need to login into other apps through ecosystem mode

    privy.io
    https://docs.privy.io/
    @privy-io/react-auth
    @privy-io/cross-app-connect
  • Version conflicts between different parts of your project

  • Runtime errors from competing implementations

  • Unexpected behaviour from mixed package versions

  • Solution

    1. Completely Remove DApp Kit Packages

    Remove all existing DApp Kit packages from your project:

    1. Completely Remove DApp Kit Packages

    VeChain Kit provides similar functionality with updated component names:

    1. Clean Installation

    After removing old packages:

    Component Mapping

    DApp Kit
    VeChain Kit

    ConnectWallet

    WalletButton

    <other dapp-kit components>

    <available through VeChain Kit exports>

    Verification

    Ensure clean migration:

    • No dapp-kit packages in package.json

    • All imports updated to use @vechain/vechain-kit

    • Component names updated to VeChain Kit equivalents

    Double delegation attempts

  • Multiple signatures on transactions

  • Incorrect transaction format

  • Solution

    1. Remove Existing Fee Delegation

    Remove all custom fee delegation logic from your application:

    • Remove fee delegation middleware

    • Remove delegation signing logic

    • Remove any custom delegation providers

    If you do not remove your own delegation then transactions could fail because your transaction will delegate 2 times, will have 2 signatures and an incorrect format.

    1. Configure VeChain Kit Fee Delegation

    Simply provide the delegation URL in the VeChainKitProvider:

    1. Delegation Options

    • delegatorUrl: Your fee delegation service URL

    • delegateAllTransactions: Set to true to delegate fees for all transactions, including VeWorld users

    Important Notes

    • VeChain Kit handles all delegation logic internally

    • No additional delegation setup required

    • Works automatically with all supported wallets

    Verification

    To verify that delegation is working correctly:

    1. Check that transactions only have one delegation signature

    2. Monitor your delegation service logs

    3. Ensure transactions complete successfully

    Common Errors

    "Transaction has multiple delegations": You still have custom delegation code active. Remove all custom delegation logic.

    "Delegation failed": Check that your delegation URL is correct and the service is running.

    data: The fetched data

  • isLoading: Boolean indicating if the request is in progress

  • isError: Boolean indicating if the request failed

  • error: Error object if the request failed

  • refetch: Function to manually trigger a new fetch

  • isRefetching: Boolean indicating if a refetch is in progress

  • Additionally, these hooks integrate with TanStack Query's global features:

    • Automatic background refetching

    • Cache invalidation

    • Optimistic updates

    • Infinite queries (for pagination)

    • Parallel queries

    • Query retrying

    • Query polling

    Query Invalidation

    All hooks use consistent query key patterns, making it easy to invalidate related queries. For example:

    Caching Behavior

    By default, most queries are configured with:

    • staleTime: How long the data remains "fresh"

    • cacheTime: How long inactive data remains in cache

    • refetchInterval: For automatic background updates (if applicable)

    These can be customized using TanStack Query's global configuration or per-hook options.

    TanStack Query
    function TokenPrices() {
      const { data: vetPrice } = useGetTokenUsdPrice('VET');
      const { data: vthoPrice } = useGetTokenUsdPrice('VTHO');
      const { data: b3trPrice } = useGetTokenUsdPrice('B3TR');
    
      return (
        <div>
          <div>VET: ${vetPrice}</div>
          <div>VTHO: ${vthoPrice}</div>
          <div>B3TR: ${b3trPrice}</div>
        </div>
      );
    }
    # Remove existing installations
    rm -rf node_modules
    rm package-lock.json # or yarn.lock
    
    # Reinstall packages
    npm install # or yarn install
    # Check what VeChain Kit expects
    npm info @vechain/vechain-kit peerDependencies
    
    # Install required peer dependencies
    npm install @chakra-ui/react@^2.8.2 @tanstack/react-query@^5.64.2 @vechain/[email protected]
    function BlockInfo() {
      const { data: block } = useCurrentBlock();
      
      return <div>Current Block: {block?.number}</div>;
    }
    const { data, isLoading, error } = useTxReceipt(txId, blockTimeout);
    function TransactionStatus({ txId }) {
      const { data: receipt, isLoading } = useTxReceipt(txId);
      
      if (isLoading) return <div>Loading...</div>;
      return <div>Transaction Status: {receipt?.reverted ? 'Failed' : 'Success'}</div>;
    }
    const events = await getEvents({
       abi,
       contractAddress,
       eventName,
       filterParams,
       mapResponse,
       nodeUrl,
    });
    import { VeChainKitProvider } from '@vechain/vechain-kit';
    
    export default function App({ Component, pageProps }: AppProps) {
        return (
            <VeChainKitProvider
                privy={{
                    appId: process.env.NEXT_PUBLIC_PRIVY_APP_ID!,
                    clientId: process.env.NEXT_PUBLIC_PRIVY_CLIENT_ID!,
                    loginMethods: ['google', 'twitter', 'sms', 'email'],
                    appearance: {
                        walletList: ['metamask', 'rainbow'],
                        accentColor: '#696FFD',
                        loginMessage: 'Select a social media profile',
                        logo: 'https://i.ibb.co/ZHGmq3y/image-21.png',
                    },
                    embeddedWallets: {
                        createOnLogin: 'all-users',
                    },
                    allowPasskeyLinking: true,
                }}
                ...
                //other props
            >
                {children}
            </VeChainKitProvider>
        );
    }
    import { usePrivy } from "@vechain/vechain-kit";
    
    const { user } = usePrivy();
    npm uninstall @vechain/dapp-kit @vechain/dapp-kit-react @vechain/dapp-kit-ui
    // X Old DApp Kit
    import { ConnectWallet } from '@vechain/dapp-kit-react';
    
    // ✅ New VeChain Kit
    import { WalletButton } from '@vechain/vechain-kit';
    rm -rf node_modules package-lock.json
    npm install
    <VeChainKitProvider
      feeDelegation={{
        delegatorUrl: process.env.NEXT_PUBLIC_DELEGATOR_URL,
        delegateAllTransactions: false
      }}
      // ... other config
    >
      <App />
    </VeChainKitProvider>
    const queryClient = useQueryClient();
    // Invalidate all blockchain queries
    queryClient.invalidateQueries({ queryKey: ['VECHAIN_KIT'] });
    // Invalidate specific query
    queryClient.invalidateQueries({ queryKey: ['VECHAIN_KIT', 'CURRENT_BLOCK'] });

    Right: A summary view under Settings > General > Terms and Policies, showing which policies they’ve accepted and when.


    Important

    Legal document agreements are tied to the wallet address, document type, document version, and the url.

    • If the version of any document is updated, users will be prompted to accept it again.

    • Agreements are stored in the browser’s local storage, meaning acceptance is per browser and device.

    • As a result, users may be prompted again if they switch browsers, devices, or clear their local storage even if they've previously agreed on another setup.

    Key Options

    Option
    Type
    Required
    Description

    allowAnalytics

    boolean

    No

    If true, prompts users with an optional tracking policy.

    cookiePolicy

    array

    No

    One or more cookie policy versions.

    privacyPolicy

    array

    No

    1

    Install @vechain/vechain-kit

    2

    Install peer dependencies

    VeChain Kit builds on a few carefully chosen libraries to deliver a better overall experience, bringing powerful tools together while keeping the integration on your side as simple as possible.

    3

    Setup provider

    Wrap your app with the VechainKitProvider at the root of your application.

    This provider brings together VeWorld’s native VeChain integration (web3) and Privy’s social login wallet support (web2).

    4

    Enjoy!

    With VeChain Kit’s snippets and primitive components, you can plug in wallet login and fetch key data much faster.

    Only supported on React and Next.js

    React query, chakra and dapp-kit are peer dependencies.

  • useUpgradeSmartAccountModal() is the hook that will allow you to open the upgrade modal, that the user will use to upgrade.

  • You can also handle this with your own UI by using the useUpgradeSmartAccount(smartAccountAddress, targetVersion: 3) hook.

  • View other useful hooks here.

    Example usage

    You can customize the color button and size of the imported modal from the kit:

    Example demo

    With UI from the Kit

    With custom UI

    Smart Account v3
    v1.5.0
    Pros of self hosting Privy:
    • No UI confirmations on users transactions

    • Allow your users to backup their keys and update security settings directly in your app

    • Targetted social login methods

    Cons:

    • Price

    • Responsibilities to correctly secure your Privy account, since it contains access to user's wallet settings

    • Your users will need to login into other apps through ecosystem mode

    2) Privy Cross App

    When users integrate VeChain-kit using "Login with VeChain" and "Ecosystem" logins (eg: Mugshot and Greencart), this will be the default connection type. It is easily recognizable because login and wallet activities will open a secured popup window where the owner of the wallet will approve the actions.

    With this type of connection, you can have social login in your app without actually paying Privy.

    3) Self-custody wallets

    This connection type allows login with self custody wallets, and is using the dapp-kit package under the hood.

    The available wallets are: VeWorld mobile, VeWorld extension, Sync2, and Wallet Connect for VeWorld mobile.

    Other wallets are available when login in with VeChain, such as Metamask, Rabby, Phantom, Coinbase Wallet, and Ranibow. That will use Privy under the hood though, which means the connection type will be "privy-cross-app".

    If you want to use vechain-kit but do not care about social login then you can skip the first login modal and directly show the "Connect Wallet" modal like this:

    When your app is opened inside VeWorld mobile wallet, VeWorld is always enforced as a login choice.

    Theme conflicts when your app uses Chakra UI
  • Missing styles or unexpected appearance

  • Solution

    Install Chakra UI

    Even if you don't use Chakra in your app, it's required as a peer dependency:

    Setup ChakraProvider

    Wrap your app with ChakraProvider and include ColorModeScript:

    One common issue, solvable by installing Chakra and defining <ColorModeScript />

    Key Requirements

    The essential setup:

    Framework Flexibility

    You can keep using whatever frontend framework you prefer. The important part is defining the ChakraProvider wrapper for VeChain Kit components to function properly.

    Common Issue

    One common issue is missing the <ColorModeScript /> component, which can cause styling inconsistencies. Always include it within your ChakraProvider.

    Utils

    Utility Hooks

    The hooks provide utility functions for interacting with the VeChain network and tokens:

    Network Utility Hooks

    • useGetChainId: Retrieves the chain ID from the genesis block of the connected network

    • useGetNodeUrl: Returns the node URL being used, either custom or default for the network

    Token Utility Hooks

    • useGetCustomTokenBalances: Fetches balances for multiple custom tokens for a given address, returning original, scaled, and formatted values

    • useGetCustomTokenInfo: Retrieves token information (name, symbol, decimals) for a custom token address.

    Legal Documents Hook

    • useLegalDocuments: Retrieves the user's agreement status for required and optional legal documents, including terms of service, privacy policy, and cookie policy.

    Usage Example

    Privy Popup Blocking

    Browser popup blocking can affect users using social login (Privy) when operations delay the signing popup.

    Problem

    When using Privy for social login (cross-app connection), browsers may block the confirmation popup if there's a delay between user action and popup trigger.

    What Causes This

    • Fetching data after button click

    • API calls before signing

    • Any async operations between click and popup

    Solution

    Pre-fetch Data Before Transaction

    Ensure all required data is loaded before the user clicks the button:

    Best Practices

    1. Load Data Early

    1. Show Loading States

    1. Avoid Async in Click Handlers

    Testing

    To test if your implementation avoids popup blocking:

    1. Use social login (Privy)

    2. Click transaction buttons

    3. Popup should appear immediately

    4. No browser blocking warnings

    Common Scenarios

    • Form submissions: Validate and prepare data before submit button is enabled

    • Token approvals: Pre-fetch allowance amounts

    • Multi-step transactions: Load all data for subsequent steps upfront

    Integration Issues

    Runtime and functionality problems that occur when integrating VeChain Kit into your application.

    Common Problems

    • Fee delegation conflicts with existing setup

    • Browser popup blocking for social login users

    • Transaction failures due to configuration issues

    • Signing delays causing security restrictions

    Quick Fixes

    • Remove existing fee delegation before using VeChain Kit's delegation

    • Pre-fetch data before triggering transactions to avoid popup blocking

    • Check configuration for proper delegation settings

    Issues in This Section

    Resolving conflicts when migrating from existing fee delegation setups and configuring VeChain Kit's delegation properly.

    Preventing browser popup blocking for social login users by properly structuring transaction flows.


    Can't find your issue? Search our or ask on .

    Embedded Wallets

    When a user initiates a connection through Privy, either directly or via cross-app integration, a secure wallet is immediately created for them. Privy implements a sophisticated key management technique known as key splitting, specifically using Shamir’s secret sharing method. This approach ensures that users retain full custody of their wallets without needing to manage any secret keys themselves. Importantly, neither Privy nor any integrated application ever accesses the user's keys; these secrets are only reconstituted directly on the user's device during the signing of messages or transactions. This process guarantees the utmost security and privacy for the user's onchain activities.

    This type of wallet created by Privy is called Embedded Wallet.

    Seamless Wallet Integration

    Users benefit from an intuitively integrated wallet management experience that aligns seamlessly with their existing accounts, removing any unnecessary technical barriers. Applications built with Privy can generate wallets automatically for new accounts, such as those registered with an email address or phone number, even before the user logs in for the first time. Additionally, Privy provides users with the option to export their wallet keys, serving as an escape mechanism should they choose to transition away from Privy’s services at any point.

    Using wallets across apps

    Privy embedded wallets are portable and can be used by any app––even apps not using Privy. Privy’s cross application wallet system supports interoperability with simple user experiences accessible to everyone, and seamlessly integrates with other Privy integrations and wallet connector solutions like RainbowKit and wagmi.

    Embedded wallets can be backed up by the user through the VeChain Kit modal in the Settings page.

    Many login methods can be attached to the Embedded Wallet.

    NFTs

    NFT Hooks

    The hooks provide tools for interacting with NFTs (Non-Fungible Tokens) on VeChain:

    NFT Data Hooks

    Ipfs

    IPFS Hooks

    The hooks provide tools for interacting with IPFS (InterPlanetary File System):

    Image Hooks

    Login

    Login Hooks

    The hooks provide authentication methods for VeChain applications:

    Authentication Hooks

    Theming

    This guide explains how to customize the VeChain Kit theme to match your app's design.

    Quick Start

    The theme system is designed to be simple - you only need to provide a base modal.backgroundColor and textColor, and all other colors are automatically derived. You can optionally customize specific aspects like overlay, buttons, and glass effects.

    CSS Framework Conflicts

    VeChain Kit uses Chakra UI internally, which can cause style conflicts with Tailwind CSS, Bootstrap, and other CSS frameworks.

    Problem: Styles Being Overridden

    Symptoms

    import { VeChainKitProvider } from '@vechain/vechain-kit';
    
    export default function App({ Component, pageProps }: AppProps) {
        return (
            <VeChainKitProvider
                legalDocuments={{
                    allowAnalytics: true, // Enables optional consent for VeChainKit tracking
    
                    cookiePolicy: [
                        {
                            displayName: 'MyApp Policy', // (Optional) Custom display label
                            url: 'https://www.myapp.com/cookie-policy',
                            version: 1, // Increment to re-prompt users
                            required: false, // Optional: User sees a checkbox to opt in
                        },
                    ],
    
                    privacyPolicy: [
                        {
                            url: 'https://www.myapp.com/privacy-policy',
                            version: 1, // Increment to re-prompt users
                            required: false, // Optional: can be skipped or rejected
                        },
                    ],
    
                    termsAndConditions: [
                        {
                            displayName: 'MyApp T&C',
                            url: 'https://www.myapp.com/terms-and-conditions',
                            version: 1, // Increment to re-prompt users
                            required: true, // Required: must be accepted to proceed
                        },
                    ],
                }}
                // ... other props
            >
                {children}
            </VeChainKitProvider>
        );
    }
    
    yarn add @vechain/vechain-kit
    $ npx create-vechain-dapp@latest
    
    ? Select template ›
        ❯ VeChain Kit Next.js Template (Chakra, React Query, SDK)
    const { open: openUpgradeSmartAccountModal } = useUpgradeSmartAccountModal({
        accentColor: '#000000',
        modalSize: 'xl',
    });
    "use-client"
    
    import { useConvertB3tr } from "@/hooks"
    import { useWallet, useUpgradeRequired, useUpgradeSmartAccountModal } from "@vechain/vechain-kit"
    
    // Example component allowing for B3TR to VOT3 conversion
    export const ConvertModal = ({ isOpen, onClose }: Props) => {
      const { account, connectedWallet, connection } = useWallet()
    
      const isSmartAccountUpgradeRequired = useUpgradeRequired(
        account?.address ?? "",
        connectedWallet?.address ?? "",
        3
      )
    
      const { open: openUpgradeModal } = useUpgradeSmartAccountModal()
      
      // A custom convert b3tr to vot3 hook
      const convertB3trMutation = useConvertB3tr({
        amount,
      })
    
      const handleConvertB3tr = useCallback(() => {
        if (connection.isConnectedWithPrivy && isSmartAccountUpgradeRequired) {
          //Open Upgrade Modal
          openUpgradeModal()
          return
        }
    
        convertB3trMutation.resetStatus()
        convertB3trMutation.sendTransaction(undefined)
      }, [isSmartAccountUpgradeRequired, convertB3trMutation, openUpgradeModal, connection])
    
    
      return (
        <button onClick={handleConvertB3tr}> Convert my B3TR to VOT3 </button>
      )
    }
    import { useDAppKitWalletModal } from '@vechain/vechain-kit';
    
    export const LoginComponent = () => {
      const { open: openWalletModal } = useDAppKitWalletModal();
    
      return (
        <Button onClick={openWalletModal}>
            Open only "Connect Wallet"
        </Button>
    )}
    yarn add @chakra-ui/[email protected]
    import React from "react";
    import ReactDOM from "react-dom/client";
    import App from "./App.tsx";
    import { VeChainKitProvider } from "@vechain/vechain-kit";
    import {
      ChakraProvider,
      ColorModeScript,
      useColorMode,
    } from "@chakra-ui/react";
    import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
    import { persister, queryClient } from "./utils/queryClient.ts";
    
    export const AppWrapper = () => {
      const { colorMode } = useColorMode();
    
      return (
        <VeChainKitProvider
          // ... your config
        >
          <App />
        </VeChainKitProvider>
      );
    };
    
    ReactDOM.createRoot(document.getElementById("root")!).render(
      <React.StrictMode>
        <PersistQueryClientProvider
          client={queryClient}
          persistOptions={{ persister }}
        >
          <ChakraProvider theme={stargateStakingTheme}>
            <ColorModeScript initialColorMode="dark" />
            <AppWrapper />
          </ChakraProvider>
        </PersistQueryClientProvider>
      </React.StrictMode>
    );
    <ChakraProvider theme={stargateStakingTheme}>
        <ColorModeScript initialColorMode="dark" />
        <AppWrapper />
    </ChakraProvider>
    Fee Delegation
    Privy Popup Blocking
    GitHub Issues
    Discord
    ​
    Sample cross app wallet flow

    One or more privacy policy versions.

    termsAndConditions

    array

    No

    One or more T&C versions.

    useNFTImage: Fetches NFT image and metadata from IPFS, handling the complete flow from token ID to final image

  • useNFTMetadataUri: Retrieves the metadata URI for an NFT using its token ID

  • Types

    Example usage

    useIpfsImage: Fetches NFT media from IPFS, supporting various image formats (JPEG, PNG, GIF, etc.)

  • useIpfsImageList: Fetches multiple IPFS images in parallel

  • useSingleImageUpload: Handles single image upload with optional compression

  • useUploadImages: Manages multiple image uploads with compression support

  • Metadata Hooks

    • useIpfsMetadata: Fetches and optionally parses JSON metadata from IPFS

    • useIpfsMetadatas: Fetches multiple IPFS metadata files in parallel

    Usage Example

    useLoginWithPasskey: Hook for authenticating using passkeys (biometric/device-based authentication)

  • useLoginWithOAuth: Hook for authenticating using OAuth providers (Google, Twitter, Apple, Discord)

  • useLoginWithVeChain: Hook for authenticating using VeChain wallet

  • Types

    Usage example

    Simplified API

    The theme configuration has been simplified to focus on what matters most:

    Base Colors

    • modal.backgroundColor (optional) - Base background color for the modal. Automatically derives:

      • Modal background (100% opacity)

      • Card background (80% opacity)

      • Sticky header background (90% opacity)

      • Secondary/tertiary colors (with opacity overlays)

      • Border colors

    • textColor (optional) - Base text color. Automatically derives:

      • Primary text (100% opacity)

      • Secondary text (70% opacity)

      • Tertiary text (50% opacity)

    Overlay Configuration

    Customize the modal overlay independently:

    Complete Example

    Here's a complete example with glass effects:

    Your Tailwind utilities stop working

  • Unexpected borders on images

  • Wrong background colors or fonts

  • Button/form styles change unexpectedly

  • Solution: CSS Layer Configuration

    Use CSS layers to control which styles take precedence:

    Framework-Specific Examples

    Tailwind CSS

    Tailwind v4

    Bootstrap

    Custom CSS

    Quick Fixes for Common Issues

    Verification Checklist

    • CSS layers defined: @layer vechain-kit, host-app

    • Framework styles wrapped in @layer host-app

    • VeChain Kit imported after layer definitions

    • Components render without style conflicts

    • No excessive !important declarations needed

    Debugging Steps

    1. Check layer order in DevTools - Styles panel shows which layer is applied

    2. Verify import order - CSS with layers must load before VeChain Kit

    3. Test in isolation - Create a minimal component to identify conflicts

    Browser Support

    CSS layers are supported in all modern browsers:

    • Chrome 99+

    • Firefox 97+

    • Safari 15.4+

    • Edge 99+

    yarn add @chakra-ui/react@^2.8.2 \
      @emotion/react@^11.14.0 \
      @emotion/styled@^11.14.0 \
      @tanstack/react-query@^5.64.2 \
      @vechain/[email protected] \
      framer-motion@^11.15.0
    'use client';
    
    import { VeChainKitProvider } from "@vechain/vechain-kit";
    
    export function Providers({ children }) {
      return (
        <VeChainKitProvider>
          {children}
        </VeChainKitProvider>
      );
    }
    "use client";
    import { WalletButton } from "@vechain/vechain-kit";
    
    
    const Demo = () => {
      return (
        <div>
           <WalletButton /> {/* Login Button */}
           <p>{account?.address}</p> {/* Address of the connected account */}
        </div>
      )
    }
    // Example usage of Utility hooks
    import {
        useGetChainId,
        useGetNodeUrl,
        useGetCustomTokenBalances,
        useGetCustomTokenInfo,
        useLegalDocuments
    } from '@vechain/vechain-kit';
    
    const ExampleComponent = () => {
        const address = "0x..."; // User's wallet address
        const tokenAddress = "0x..."; // Custom token address
    
        // Get network information
        const { data: chainId } = useGetChainId();
        const nodeUrl = useGetNodeUrl();
    
        // Get custom token information
        const { data: tokenInfo } = useGetCustomTokenInfo(tokenAddress);
    
        // Get token balances
        const { 
            data: tokenBalances,
            isLoading,
            error 
        } = useGetCustomTokenBalances(address);
        
        // Get legal documents data
        const {
            documentsNotAgreed,
            documents,
            agreements,
            hasAgreedToRequiredDocuments,
        } = useLegalDocuments();
    
        console.log(
            'Chain ID:', chainId,
            'Node URL:', nodeUrl,
            'Token Info:', tokenInfo,
            'Token Balances:', tokenBalances,
            'Has Agreed to Required Documents:', hasAgreedToRequiredDocuments,
            'User Document Agreements:', agreements,
            'Documents Not Agreed:', documentsNotAgreed,
            'All Legal Documents:', documents
        );
    
    
        return (
            // Your component JSX here
        );
    };
    
    export default ExampleComponent;
    
    /*
    Note: These hooks require:
    - A valid thor connection
    - Appropriate network configuration
    - Valid input parameters where applicable
    
    The token-related hooks work with ERC20 tokens and return:
    - Original values (raw from contract)
    - Scaled values (adjusted for decimals)
    - Formatted values (human-readable)
    */
    // ✅ Good: Pre-fetch data
    const { data } = useQuery(['someData'], fetchSomeData);
    const sendTx = () => sendTransaction(data);
    
    // ❌ Bad: Fetching data during the transaction
    const sendTx = async () => {
      const data = await fetchSomeData();  // This delay causes popup blocking
      return sendTransaction(data);
    };
    // Load data when component mounts or when form changes
    const { data: gasPrice } = useQuery(['gasPrice'], fetchGasPrice, {
        staleTime: 30000 // Cache for 30 seconds
    });
    // Transaction handler is instant
    const handleTransaction = () => {
        sendTransaction({
            gasPrice,
            // ... other pre-loaded data
        });
    };
    const MyComponent = () => {
        const { data, isLoading } = useQuery(['requiredData'], fetchData);
    
        return (
          <button 
            onClick={() => sendTransaction(data)}
            disabled={isLoading}
          >
            {isLoading ? 'Preparing...' : 'Send Transaction'}
          </button>
        );
      };
    
    // ❌ Avoid
    onClick={async () => {
      const result = await someAsyncOperation();
      sendTransaction(result);
    }}
    
    // ✅ Better
    onClick={() => {
      sendTransaction(preLoadedData);
    }}
    // NFT Types
    interface NFTMetadata {
        name: string;
        description: string;
        image: string;
        attributes: {
            trait_type: string;
            value: string | number;
        }[];
    }
    
    interface NFTImageHookParams {
        address: string;
        contractAddress: string;
    }
    
    interface NFTMetadataUriParams {
        tokenId: string;
        contractAddress: string;
    }
    
    interface NFTImageHookResult {
        imageData: string | null;
        imageMetadata: NFTMetadata | null;
        tokenID: string | null;
        isLoading: boolean;
        error: Error | null;
    }
    
    interface NFTMetadataUriResult {
        data: string | null;
        isLoading: boolean;
        error: Error | null;
    }
    // Example usage of NFT hooks
    import { useNFTImage, useNFTMetadataUri } from '@vechain/vechain-kit';
    
    const ExampleComponent = () => {
        const walletAddress = "0x...";
        const tokenId = "1";
        const contractAddress = "0x...";
    
        // Get complete NFT data including image
        const { 
            imageData,
            imageMetadata,
            tokenID,
            isLoading: isLoadingNFT,
            error: nftError 
        } = useNFTImage({
            address: walletAddress,
            contractAddress: contractAddress
        });
    
        // Get just the metadata URI
        const {
            data: metadataUri,
            isLoading: isLoadingUri,
            error: uriError
        } = useNFTMetadataUri({
            tokenId,
            contractAddress
        });
    
        // Handle loading states
        if (isLoadingNFT || isLoadingUri) {
            return <div>Loading NFT data...</div>;
        }
    
        // Handle errors
        if (nftError || uriError) {
            return <div>Error: {nftError?.message || uriError?.message}</div>;
        }
    
        console.log(
            'NFT Image:', imageData,
            'NFT Metadata:', imageMetadata,
            'Token ID:', tokenID,
            'Metadata URI:', metadataUri
        );
    
        // Example of using the NFT data
        return (
            <div>
                {imageData && (
                    <img 
                        src={imageData} 
                        alt={imageMetadata?.name || 'NFT'} 
                    />
                )}
                {imageMetadata && (
                    <div>
                        <h2>{imageMetadata.name}</h2>
                        <p>{imageMetadata.description}</p>
                        {imageMetadata.attributes?.map((attr, index) => (
                            <div key={index}>
                                {attr.trait_type}: {attr.value}
                            </div>
                        ))}
                    </div>
                )}
            </div>
        );
    };
    
    export default ExampleComponent;
    
    /*
    Note: These hooks require:
    - Valid thor connection
    - Network type configuration
    - Valid IPFS gateway for image fetching
    - The hooks handle the complete flow:
      1. Get token ID from address
      2. Get metadata URI from token ID
      3. Fetch metadata from IPFS
      4. Fetch image from IPFS
    
    Types:
    interface NFTMetadata {
        name: string;
        description: string;
        image: string;
        attributes: {
            trait_type: string;
            value: string;
        }[];
    }
    */
    // Example usage of IPFS hooks
    import { 
        useIpfsImage, 
        useIpfsMetadata,
        useSingleImageUpload,
        useUploadImages
    } from '@vechain/vechain-kit';
    
    const ExampleComponent = () => {
        // Fetch an NFT image from IPFS
        const { 
            data: imageData,
            isLoading: isImageLoading,
            error: imageError 
        } = useIpfsImage("ipfs://...");
    
        // Fetch metadata from IPFS
        const { 
            data: metadata,
            isLoading: isMetadataLoading,
            error: metadataError 
        } = useIpfsMetadata<{ 
            name: string; 
            description: string 
        }>("ipfs://...", true); // parse as JSON
    
        // Handle single image upload
        const {
            uploadedImage,
            onUpload: handleSingleUpload,
            onRemove: handleSingleRemove,
            isUploading: isSingleUploading
        } = useSingleImageUpload({
            compressImage: true,
            defaultImage: undefined,
            maxSizeMB: 0.4,
            maxWidthOrHeight: 1920
        });
    
        // Handle multiple image uploads
        const {
            uploadedImages,
            onUpload: handleMultipleUpload,
            onRemove: handleMultipleRemove,
            isUploading: isMultipleUploading
        } = useUploadImages({
            compressImages: true,
            defaultImages: [],
            maxSizeMB: 0.4,
            maxWidthOrHeight: 1920
        });
    
        // Example upload handler
        const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
            const file = event.target.files?.[0];
            if (file) {
                await handleSingleUpload(file);
            }
        };
    
        // Example multiple files upload handler
        const handleMultipleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
            const files = Array.from(event.target.files || []);
            await handleMultipleUpload(files);
        };
    
        console.log(
            'IPFS Image:', imageData,
            'Loading Image:', isImageLoading,
            'Metadata:', metadata,
            'Loading Metadata:', isMetadataLoading,
            'Uploaded Single Image:', uploadedImage,
            'Single Upload in Progress:', isSingleUploading,
            'Uploaded Multiple Images:', uploadedImages,
            'Multiple Upload in Progress:', isMultipleUploading
        );
    
        return (
            // Your component JSX here
        );
    };
    
    export default ExampleComponent;
    
    /*
    Note: These hooks require:
    - Valid IPFS gateway configuration
    - Network type configuration
    - For image hooks:
      - Supported formats: JPEG, PNG, GIF, BMP, TIFF, WebP, SVG
      - Maximum file size: 10MB
    - For upload hooks:
      - Default compression to 0.4MB
      - Max width/height: 1920px
      - Web Worker support for compression
    */
    // OAuth Types
    type OAuthProvider = 'google' | 'twitter' | 'apple' | 'discord';
    
    interface OAuthOptions {
        provider: OAuthProvider;
    }
    
    interface UseLoginWithOAuthReturn {
        initOAuth: (options: OAuthOptions) => Promise<void>;
    }
    
    interface UseLoginWithPasskeyReturn {
        loginWithPasskey: () => Promise<void>;
    }
    
    interface UseLoginWithVeChainReturn {
        login: () => Promise<void>;
    }
    // Example usage of Login hooks
    import { 
        useLoginWithPasskey,
        useLoginWithOAuth,
        useLoginWithVeChain 
    } from '@vechain/vechain-kit';
    
    const ExampleComponent = () => {
        // Passkey authentication
        const { 
            loginWithPasskey,
        } = useLoginWithPasskey();
    
        // OAuth authentication
        const {
            initOAuth,
        } = useLoginWithOAuth();
    
        // VeChain wallet authentication
        const {
            login: loginWithVeChain,
        } = useLoginWithVeChain();
    
        const handlePasskeyLogin = async () => {
            try {
                await loginWithPasskey();
                console.log("Passkey login successful");
            } catch (error) {
                console.error("Passkey login failed:", error);
            }
        };
    
        const handleOAuthLogin = async (provider: OAuthProvider) => {
            try {
                await initOAuth({ provider });
                console.log(`${provider} OAuth login initiated`);
            } catch (error) {
                console.error("OAuth login failed:", error);
            }
        };
    
        const handleVeChainLogin = async () => {
            try {
                await loginWithVeChain();
                console.log("VeChain login successful");
            } catch (error) {
                console.error("VeChain login failed:", error);
            }
        };
    
        return (
            <div>
                {/* Passkey Login */}
                <button onClick={handlePasskeyLogin}>
                    Login with Passkey
                </button>
    
                {/* OAuth Login Options */}
                <button onClick={() => handleOAuthLogin('google')}>
                    Login with Google
                </button>
                <button onClick={() => handleOAuthLogin('twitter')}>
                    Login with Twitter
                </button>
                <button onClick={() => handleOAuthLogin('apple')}>
                    Login with Apple
                </button>
                <button onClick={() => handleOAuthLogin('discord')}>
                    Login with Discord
                </button>
    
                {/* VeChain Wallet Login */}
                <button onClick={handleVeChainLogin}>
                    Login with VeChain Wallet
                </button>
            </div>
        );
    };
    
    export default ExampleComponent;
    
    /*
    Note: These hooks require:
    - Privy configuration for OAuth and Passkey
    - Valid VeChain network configuration
    - For VeChain login:
      - Valid VECHAIN_PRIVY_APP_ID
      - Proper error handling for popup blockers
      - Mobile browser compatibility handling
    */
    <VeChainKitProvider
        theme={{
            modal: {
                backgroundColor: isDarkMode ? '#1f1f1e' : '#ffffff',
            },
            textColor: isDarkMode ? 'rgb(223, 223, 221)' : '#2e2e2e',
            overlay: {
                backgroundColor: 'rgba(0, 0, 0, 0.6)',
                blur: 'blur(3px)',
            },
            buttons: {
                secondaryButton: {
                    bg: 'rgba(255, 255, 255, 0.1)',
                    color: '#ffffff',
                    border: 'none',
                },
            },
            effects: {
                glass: {
                    enabled: true,
                    intensity: 'low',
                },
            },
        }}
        // ... other props
    >
        {children}
    </VeChainKitProvider>
    overlay: {
        backgroundColor: 'rgba(0, 0, 0, 0.6)', // Overlay background color
        blur: 'blur(10px)', // Overlay blur effect
    }
    import type { VechainKitThemeConfig } from '@vechain/vechain-kit';
    
    const theme: VechainKitThemeConfig = {
        modal: {
            backgroundColor: isDarkMode ? '#1f1f1e' : '#ffffff',
            border: "1px solid #00000",
            // backdropFilter?: string; // Backdrop filter for modal dialog (e.g., "blur(10px)")
            // borderRadius?: string; // Modal dialog border radius (e.g., "24px", "1rem") - deprecated, use rounded instead
            // rounded?: string | number; // Border radius (Chakra UI rounded prop: "sm", "md", "lg", "xl", "2xl", "3xl", "full", or number)
        },
        textColor: isDarkMode ? 'rgb(223, 223, 221)' : '#2e2e2e',
        overlay: {
            backgroundColor: isDarkMode
                ? 'rgba(0, 0, 0, 0.6)'
                : 'rgba(0, 0, 0, 0.4)',
            blur: 'blur(3px)',
        },
        buttons: {
            primaryButton: {
                bg: isDarkMode ? '#3182CE' : '#2B6CB0',
                color: 'white',
                border: 'none',
            },    
            secondaryButton: {
                bg: isDarkMode ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.1)',
                color: isDarkMode ? 'rgb(223, 223, 221)' : '#2e2e2e',
                border: 'none',
                // backdropFilter?: string; // Optional backdrop filter (e.g., "blur(10px)")
                // rounded?: string | number; // Border radius (Chakra UI rounded prop: "sm", "md", "lg", "xl", "2xl", "3xl", "full", or number)
            },
            loginButton: {
                bg: 'transparent',
                color: isDarkMode ? 'white' : '#1a1a1a',
                border: isDarkMode
                    ? '1px solid rgba(255, 255, 255, 0.1)'
                    : '1px solid #ebebeb',
            },
        },
        fonts: {
            family: 'Inter, sans-serif',
            sizes: {
                small: '12px',
                medium: '14px',
                large: '16px',
            },
            weights: {
                normal: 400,
                medium: 500,
                bold: 700,
            },
        },
        effects: {
            glass: {
                enabled: true,
                intensity: 'low',
            },
        },
    };
    
    <VeChainKitProvider theme={theme} {...otherProps}>
        {children}
    </VeChainKitProvider>;
    /* In your global CSS file (e.g., globals.css) */
    
    /* 1. Define layers with explicit priority */
    @layer vechain-kit, host-app;
    
    /* 2. Wrap your framework in host-app layer */
    @layer host-app {
        @tailwind base;
        @tailwind components;
        @tailwind utilities;
    
        /* Your custom styles here */
    }
    @layer vechain-kit, host-app;
    
    @layer host-app {
        @tailwind base;
        @tailwind components;
        @tailwind utilities;
    }
    @layer vechain-kit, host-app;
    
    /* Import Tailwind v4 */
    @import "tailwindcss";
    
    /* Your custom styles */
    @layer host-app {
        /* Custom overrides */
    }
    @layer vechain-kit, host-app;
    
    @layer host-app {
        @import 'bootstrap/dist/css/bootstrap.css';
    }
    @layer vechain-kit, host-app;
    
    @layer host-app {
        body {
            /* Your body styles */
        }
    
        .your-components {
            /* Component styles */
        }
    }
    @layer host-app {
        /* Fix image borders */
        img {
            border-style: solid !important;
        }
    
        /* Ensure body styles persist */
        html body {
            background: var(--your-bg);
            font-family: var(--your-font);
        }
    }

    Upgrade VeChain Kit from 1.x to 2.x

    What's Changed in 2.0

    Major Breaking Changes

    • Connex Removal: useConnex is replaced with useThor

    • New Contract Interaction Patterns: Introduction of useCallClause and executeMultipleClausesCall

    • Enhanced Transaction Building: New useBuildTransaction hook with improved type safety

    • Improved Type Safety: Better TypeScript support with stricter typing

    • useEvents() hook was refactored,

    • The following hooks were also removed in order to improve permeances.

    Deprecated Hooks

    Utils Hooks

    • useRoundAppVotes

    • useSustainabilityActions

    Galaxy Member Hooks

    useCallClause is for reading data from smart contracts with automatic caching and refetching

    executeMultipleClausesCall is to execute multiple contract calls in a single batch

    It might require to rm -rf node_modules yarn.lock && yarn

    Migration Path

    Preparation Steps

    1. Create a git branch for migration

    2. Identify all places where useConnex is used (This is the main breaking change and easiest to find)

    3. Run tsc compiler to see all broken references

    4. Fix the compiler errors and migrate incrementally. The new methods provide type-safe returns that will guide you

    Getting Help

    • Documentation: Refer to individual migration guide sections

    • GitHub Issues:

    Next Steps

    1. Start with the guide to update your core dependencies and imports

    2. Review to understand new interaction methods

    3. Apply for optimal performance

    4. Consult if you encounter issues

    Text Records (avatar & co.)

    With every name come a set of records. These records are key value pairs that can be used to store information about the profile. Think of this as a user's digital backpack. Utalized for storage of preferences, public details, and more.

    Types of Records

    Here are some of the most commonly used records:

    Name
    Usage
    Reference
    Example

    Other Records

    Currently there are a few records that have been standardised. However you are welcome to store any key value pair you desire. We generally recommend to stick to a pattern, or prefix things with your app or protocol (eg. com.discord, or org.reddit), as such to avoid collisions.

    Header/Banner Record

    One of the newer standardised records is the "header" record. This header record, similar to the avatar record, accepts any IPFS, Arweave, EIP155, or regular URL to an image resource. The image is then displayed as a banner on the profile page and tends to be in a 1:3 aspect ratio.

    Setting Records

    When records are loaded they are loaded from the resolver responsible for the name. As resolvers are user controlled, we cannot guarantee a write function is available. This makes it a more in-depth process to update a users records.

    This is protocol build by ENS domains and supported by veDelegate. Follow for more information.

    Keep reading how to integrate with the VeChain Kit

    Transactions

    Transaction Hooks

    The hooks provide tools for handling transactions on VeChain:

    Core Transaction Hooks

    • useSendTransaction: Core hook for sending any transaction, supporting both Privy and VeChain wallets

    • useTransferERC20: Hook for sending ERC20 token transfers

    • useTransferVET: Hook for sending native VET token transfers

    Types

    Usage example

    vetDomains

    VetDomains Hooks

    The hooks provide tools for interacting with VET domains and their functionality:

    Domain Resolution Hooks

    • useVechainDomain: Resolves VET domains to addresses and vice versa, returning domain info and validation status

    • useIsDomainProtected: Checks if a domain is protected from claiming

    • useGetDomainsOfAddress: Gets all domains owned by an address, with optional parent domain filtering

    Domain Record Management Hooks

    • useGetTextRecords: Gets all text records for a domain

    • useGetAvatar: Gets the avatar URL for a domain. This hook will return directly the URL of the image, removing the need for developers to convert the URI to URL manually. The response can be null if the domain name does not exist or if there is no avatar attached to this domain.

    • useGetAvatarOfAddress : This hook will check if the address has any primary domain name set, if yes it will fetch and return the avatar URL (again, no need to manually convert URI to URL, the hook does it). If there is no domain name attached to it or if there is no avatar attached to the domain, it will return the Picasso image.

    Subdomain Management Hooks

    • useClaimVeWorldSubdomain: Claims a VeWorld subdomain with transaction handling and success/error callbacks

    Usage Example

    Transaction Modal

    Use our pre built TransactionModal or TransactionToast components to show your users the progress and outcome of the transaction, or build your own UX and UI.

    Usage example

    General

    Common issues and solutions during migration from 1.x to 2.0

    TypeScript Compilation Errors

    Type Mismatch Errors

    BigInt Serialization Errors

    React Query Issues

    Cache Invalidation Not Working

    Contract Interaction Issues

    Migration from manual ABI lookup:

    Transaction Building Fails

    Performance Issues

    Too Many Network Requests

    Network Issues

    RPC Endpoint Problems

    Testing Issues

    Mocking Problems

    Provider Configuration

    This guide covers how to set up and configure the VeChainKitProvider in your application.

    Basic Setup

    Wrap your app with the VeChainKitProvider:

    Wallet

    The useWallet hook provides a unified interface for managing wallet connections in a VeChain application, supporting multiple connection methods including social logins (via Privy), direct wallet connections (via DappKit), and cross-application connections.

    This will be the hook you will use more frequently and it provides quite a few useful informations.

    Usage

    Fee Delegation

    Fee Delegation is mostly meant to be implemented to support end-user-experience, removing the need to purchase/collect gas tokens and remove need to pay for the applications activities.

    Currently, it is mandatory to sponsor transaction for users that use social login. You can deploy your own custom solution or use .

    FEE_DELEGATION_URL

    'use client';
    
    import {
        useWallet,
        useSendTransaction,
        useTransactionModal,
        TransactionModal,
        getConfig
    } from '@vechain/vechain-kit';
    import { IB3TR__factory } from '@vechain/vechain-kit/contracts';
    import { humanAddress } from '@vechain/vechain-kit/utils';
    import { useMemo, useCallback } from 'react';
    
    export function TransactionExamples() {
        const { account } = useWallet();
        const b3trMainnetAddress = getConfig("main").b3trContractAddress;
        
        const clauses = useMemo(() => {
            const B3TRInterface = IB3TR__factory.createInterface();
    
            const clausesArray: any[] = [];
            clausesArray.push({
                to: b3trMainnetAddress,
                value: '0x0',
                data: B3TRInterface.encodeFunctionData('transfer', [
                    "0x0, // receiver address
                    '0', // 0 B3TR (in wei)
                ]),
                comment: `This is a dummy transaction to test the transaction modal. Confirm to transfer ${0} B3TR to ${humanAddress("Ox0")}`,
                abi: B3TRInterface.getFunction('transfer'),
            });
    
            return clausesArray;
        }, [connectedWallet?.address]);
    
        const {
            sendTransaction,
            status,
            txReceipt,
            resetStatus,
            isTransactionPending,
            error,
        } = useSendTransaction({
            signerAccountAddress: account?.address ?? '',
        });
    
        const {
            open: openTransactionModal,
            close: closeTransactionModal,
            isOpen: isTransactionModalOpen,
        } = useTransactionModal();
    
        // This is the function triggering the transaction and opening the modal
        const handleTransaction = useCallback(async () => {
            openTransactionModal();
            await sendTransaction(clauses);
        }, [sendTransaction, clauses, openTransactionModal]);
        
        const handleTryAgain = useCallback(async () => {
            resetStatus();
            await sendTransaction(clauses);
        }, [sendTransaction, clauses, resetStatus]);
    
        return (
            <>
                <button
                    onClick={handleTransactionWithModal}
                    isLoading={isTransactionPending}
                    isDisabled={isTransactionPending}
                >
                    Send B3TR
                </button>
    
                <TransactionModal
                    isOpen={isTransactionModalOpen}
                    onClose={closeTransactionModal}
                    status={status}
                    txReceipt={txReceipt}
                    txError={error}
                    onTryAgain={handleTryAgain}
                    uiConfig={{
                        title: 'Test Transaction',
                        description: `This is a dummy transaction to test the transaction modal. Confirm to transfer ${0} B3TR to ${
                            account?.address
                        }`,
                        showShareOnSocials: true,
                        showExplorerButton: true,
                        isClosable: true,
                    }}
                />
            </>
        );
    }
    
    // Error: Type 'string' is not assignable to type '`0x${string}`'
    const address: `0x${string}` = userAddress;
    
    // ✅ Solution
    const contractAddress = getConfig(networkType).contractAddress as `0x${string}`;
    const method = 'convertedB3trOf' as const;
    
    // ✅ Validate addresses
    import { humanAddress } from '@vechain/vechain-kit/utils';
    const validatedAddress = humanAddress(address);

    useGMbalance

  • useB3trToUpgrade

  • useB3trToUpgradeToLevel

  • useGetNodeIdAttached

  • useGetTokenIdAttachedToNode

  • useGMMaxLevel

  • useParticipatedInGovernance

  • useTokenIdByAccount

  • useNFTImage

  • useB3trDonated

  • useGMBaseUri

  • useSelectedTokenId

  • useIsGMClaimable

  • useSelectedGmNft

  • useLevelOfToken

  • useNFTMetadataUri

  • NodeManagement

    • useGetNodeManager

    • useIsNodeHolder

    • useUserXNodes

    VeBetterPassport

    • useAccountLinking

    • usePassportChecks

    • useUserDelegation

    • useUserStatus

    • useAppSecurityLevel

    • useGetCumulativeScoreWithDecay

    • useGetDelegatee

    • useGetDelegator

    • useGetEntitiesLinkedToPassport

    • useGetPassportForEntity

    • useGetPendingDelegationsDelegateePOV

    • useGetPendingDelegationsDelegatorPOV

    • useGetPendingLinkings

    • useIsEntity

    • useIsPassportCheckEnabled

    • useIsPassport

    • useParticipationScoreThreshold

    • useSecurityMultiplier

    • useThresholdParticipationScore

    • useThresholdParticipationScoreAtTimepoint

    • useIsBlacklisted

    • useIsWhitelisted

    • useUserRoundScore

    VBD VoterRewards:

    • useLevelMultiplier

    X2Earn Apps:

    • useUserVotesInAllRounds

    • useUserTopVotedApps

    • useXNode

    • useAppAdmin

    • useAppExists

    • useAppsEligibleInNextRound

    • useGetX2EarnAppAvailableFunds

    • useXAppsMetadataBaseUri

    • useXNodeCheckCooldown

    XAllocation Voting

    • useAllocationAmount

    • useXAppVotesQf

  • Verify functionality as you fix each error

  • Ensure you have adequate test coverage before starting

  • more here
    Report issues
    API Changes
    Contract Patterns
    Best Practices
    Troubleshooting
    https://streamable.com/hyritwstreamable.com
    https://streamable.com/wrpzo4streamable.com
  • useGetResolverAddress: Gets the resolver contract address for a domain

  • useUpdateTextRecord: Updates text records for a domain with transaction handling

  • Next.js Configuration

    For Next.js applications, dynamically import the provider to avoid SSR issues:

    Complete Configuration Example

    Here's a comprehensive example with all available options:

    Configuration Options

    Network Configuration

    Fee Delegation

    Configure transaction fee sponsorship:

    Login Methods

    Configure available authentication methods with a flexible grid layout:

    Grid Layout: The gridColumn property determines the width of each login option in the modal (based on a 4-column grid).

    Privy Integration (Optional)

    To enable social login methods with your own Privy account:

    Ecosystem Apps Configuration

    Customize which ecosystem apps appear in the login modal:

    Best Practices

    1. Dynamic Import: Always use dynamic import in Next.js to avoid SSR issues

    2. Environment Variables: Store sensitive configuration in environment variables

    3. Fee Delegation: Consider your fee delegation strategy based on user experience needs

    4. Login Methods: Choose login methods that match your target audience

    5. Metadata: Provide clear app metadata for wallet connection requests

    Next Steps

    • Implement Authentication Methods

    • Customize UI Theme

    • Handle Wallet Interactions

    // Transaction Types
    type TransactionStatus = 'ready' | 'pending' | 'waitingConfirmation' | 'success' | 'error';
    
    type TransactionStatusErrorType = {
        type: 'UserRejectedError' | 'RevertReasonError';
        reason: string;
    };
    
    type EnhancedClause = {
        to: string;
        value?: string;
        data?: string;
        comment?: string;
        abi?: any;
    };
    
    interface TransactionHookReturnValue {
        sendTransaction: (clauses?: TransactionClause[]) => Promise<void>;
        isTransactionPending: boolean;
        isWaitingForWalletConfirmation: boolean;
        txReceipt: TransactionReceipt | null;
        status: TransactionStatus;
        resetStatus: () => void;
        error?: TransactionStatusErrorType;
    }
    
    interface TransferVETParams {
        fromAddress: string;
        receiverAddress: string;
        amount: string;
        onSuccess?: () => void;
        onError?: (error: Error) => void;
    }
    
    interface TransferERC20Params {
        fromAddress: string;
        receiverAddress: string;
        amount: string;
        tokenAddress: string;
        tokenName: string;
        onSuccess?: () => void;
        onError?: (error: Error) => void;
    }
    
    interface SendTransactionParams {
        signerAccountAddress: string;
        clauses: EnhancedClause[];
        onTxConfirmed?: () => void;
        onTxFailedOrCancelled?: () => void;
    }
    // Example usage of Transaction hooks
    import { 
        useSendTransaction,
        useTransferERC20,
        useTransferVET 
    } from '@vechain/vechain-kit';
    
    const ExampleComponent = () => {
        // Example of sending VET
        const { 
            sendTransaction: sendVET,
            isTransactionPending: isVETPending,
            status: vetStatus,
            error: vetError 
        } = useTransferVET({
            fromAddress: "0x...",
            receiverAddress: "0x...",
            amount: "1.5",
            onSuccess: () => console.log("VET transfer successful"),
            onError: () => console.log("VET transfer failed")
        });
    
        // Example of sending ERC20 tokens
        const {
            sendTransaction: sendToken,
            isTransactionPending: isTokenPending,
            status: tokenStatus,
            error: tokenError
        } = useTransferERC20({
            fromAddress: "0x...",
            receiverAddress: "0x...",
            amount: "100",
            tokenAddress: "0x...",
            tokenName: "TOKEN",
            onSuccess: () => console.log("Token transfer successful"),
            onError: () => console.log("Token transfer failed")
        });
    
        // Example of custom transaction
        const {
            sendTransaction: sendCustomTx,
            isTransactionPending: isCustomPending,
            status: customStatus,
            error: customError
        } = useSendTransaction({
            signerAccountAddress: "0x...",
            clauses: [
                {
                    to: "0x...",
                    value: "0x0",
                    data: "0x...",
                    comment: "Custom transaction"
                }
            ],
            onTxConfirmed: () => console.log("Custom transaction successful"),
            onTxFailedOrCancelled: () => console.log("Custom transaction failed")
        });
    
        const handleVETTransfer = async () => {
            try {
                await sendVET();
            } catch (error) {
                console.error("VET transfer error:", error);
            }
        };
    
        const handleTokenTransfer = async () => {
            try {
                await sendToken();
            } catch (error) {
                console.error("Token transfer error:", error);
            }
        };
    
        const handleCustomTransaction = async () => {
            try {
                await sendCustomTx();
            } catch (error) {
                console.error("Custom transaction error:", error);
            }
        };
    
        return (
            <div>
                <button 
                    onClick={handleVETTransfer} 
                    disabled={isVETPending}
                >
                    Send VET
                </button>
                <button 
                    onClick={handleTokenTransfer} 
                    disabled={isTokenPending}
                >
                    Send Tokens
                </button>
                <button 
                    onClick={handleCustomTransaction} 
                    disabled={isCustomPending}
                >
                    Send Custom Transaction
                </button>
    
                {/* Status displays */}
                {vetStatus === 'pending' && <div>VET Transfer Pending...</div>}
                {tokenStatus === 'pending' && <div>Token Transfer Pending...</div>}
                {customStatus === 'pending' && <div>Custom Transaction Pending...</div>}
    
                {/* Error displays */}
                {vetError && <div>VET Error: {vetError.reason}</div>}
                {tokenError && <div>Token Error: {tokenError.reason}</div>}
                {customError && <div>Custom Error: {customError.reason}</div>}
            </div>
        );
    };
    
    export default ExampleComponent;
    
    /*
    Note: These hooks require:
    - Valid thor connection
    - Network configuration
    - Valid wallet connection
    - For ERC20 transfers: valid token contract address
    - All amounts should be in their base units (e.g., wei for VET)
    */
    /**
     * Domain Management Hooks
     * The hooks provide tools for interacting with VET domains and their functionality
     */
    
    // Domain Resolution Hooks
    - `useVechainDomain(addressOrDomain: string)`: 
      Returns { address?: string; domain?: string; isValidAddressOrDomain: boolean }
      // Resolves VET domains to addresses and vice versa
    
    - `useIsDomainProtected(domain: string)`: 
      Returns boolean
      // Checks if a domain is protected from claiming
    
    - `useGetDomainsOfAddress(address: string, parentDomain?: string)`: 
      Returns { domains: Array<{ name: string }> }
      // Gets all domains owned by an address
    
    // Domain Record Management Hooks
    - `useGetTextRecords(domain: string)`: 
      Returns TextRecords object
      // Gets all text records for a domain
    
    - `useGetAvatar(domain: string)`: 
      Returns string | null
      // Gets the avatar URL for a domain
    
    - `useGetResolverAddress(domain: string)`: 
      Returns string
      // Gets the resolver contract address for a domain
    
    - `useUpdateTextRecord({
        onSuccess?: () => void,
        onError?: (error: Error) => void,
        signerAccountAddress?: string,
        resolverAddress: string
      })`: 
      Returns { sendTransaction, isTransactionPending, error }
      // Updates text records for a domain
    
    // Subdomain Management Hooks
    - `useClaimVeWorldSubdomain({
        subdomain: string,
        domain: string,
        onSuccess?: () => void,
        onError?: (error: Error) => void,
        alreadyOwned?: boolean
      })`: 
      Returns { sendTransaction, isTransactionPending, error }
      // Claims a VeWorld subdomain
    
    // Example Usage
    const ExampleComponent = () => {
        const address = "0x...";
        const domain = "example.vet";
        
        // Resolve domain or address
        const { data: domainInfo } = useVechainDomain(address);
        
        // Check domain protection
        const { data: isProtected } = useIsDomainProtected(domain);
        
        // Get owned domains
        const { data: ownedDomains } = useGetDomainsOfAddress(address);
        
        // Get domain records
        const { data: textRecords } = useGetTextRecords(domain);
        const { data: avatar } = useGetAvatar(domain);
        const { data: avatarOfAddress } = useGetAvatarOfAddress(address);
        
        // Update domain records
        const { sendTransaction: updateRecord } = useUpdateTextRecord({
            onSuccess: () => console.log("Record updated"),
            resolverAddress: "0x..."
        });
        
        // Claim subdomain
        const { sendTransaction: claimSubdomain } = useClaimVeWorldSubdomain({
            subdomain: "mysub",
            domain: "veworld.vet",
            onSuccess: () => console.log("Subdomain claimed")
        });
    
        console.log(
            'Domain Info:', domainInfo,
            'Is Protected:', isProtected,
            'Owned Domains:', ownedDomains,
            'Text Records:', textRecords,
            'Avatar:', avatar
        );
    
        return (
            // Your component JSX
        );
    };
    
    /*
    Requirements:
    - All hooks require a valid thor connection
    - Appropriate network configuration must be set
    - Valid input parameters must be provided
    - Most hooks are part of the @tanstack/react-query ecosystem
    
    Additional Notes:
    - Domain resolution works for both .vet domains and addresses
    - Text records can include various fields like email, description, url, etc.
    - Subdomain claiming is specific to veworld.vet domains
    - Protection status affects whether a domain can be claimed or transferred
    */
    import { hashFn } from 'wagmi/query';
    // Error: Do not know how to serialize a BigInt
    
    // ✅ Solution: set wagmi's hashfn as default queryKeyHashFn
    export const queryClient = new QueryClient({
      defaultOptions: {
        queries: {
          queryKeyHashFn: hashFn,
          retry: 0,
          retryOnMount: false,
          staleTime: 30000,
          // other options
        },
      },
    })
    
    // ✅ Good: Consistent query key structure
    const queryKey = getCallClauseQueryKeyWithArgs({
      abi: VOT3__factory.abi,
      address: contractAddress,
      method: 'balanceOf' as const,
      args: [userAddress],
    });
    
    // ✅ Proper invalidation
    queryClient.invalidateQueries({ queryKey });
    // Old pattern
    const functionAbi = vot3Abi.find((e) => e.name === "convertedB3trOf");
    const res = await thor.account(contractAddress).method(functionAbi).call(address);
    
    // ✅ New pattern
    import { VOT3__factory } from '@vechain/vechain-kit/contracts';
    return useCallClause({
      abi: VOT3__factory.abi,
      address: contractAddress,
      method: 'convertedB3trOf' as const,
      args: [address ?? ''],
      queryOptions: { enabled: !!address },
    });
    // Old manual transaction building
    const buildConvertTx = (thor: Connex.Thor, amount: string) => {
      const functionAbi = abi.find((e) => e.name === "convertToVOT3");
      const clause = thor.account(contractAddress).method(functionAbi).asClause(amount);
      return { ...clause, comment: `Convert ${amount}`, abi: functionAbi };
    };
    
    // ✅ New clause building
    import { VOT3__factory } from '@vechain/vechain-kit/contracts';
    const buildConvertTx = (thor: ThorClient, amount: string): EnhancedClause => {
      const { clause } = thor.contracts
        .load(contractAddress, VOT3__factory.abi)
        .clause.convertToVOT3(ethers.parseEther(amount));
    
      return {
        ...clause,
        comment: `Convert ${humanNumber(amount)} B3TR to VOT3`,
      };
    };
    // ✅ Batch multiple calls
    const useTokenData = (tokenAddress: string) => {
      return useQuery({
        queryKey: ['TOKEN_DATA', tokenAddress],
        queryFn: async () => {
          const results = await executeMultipleClausesCall({
            thor,
            calls: [
              { abi: ERC20__factory.abi, functionName: 'symbol', address: tokenAddress, args: [] },
              { abi: ERC20__factory.abi, functionName: 'decimals', address: tokenAddress, args: [] },
              { abi: ERC20__factory.abi, functionName: 'totalSupply', address: tokenAddress, args: [] },
            ],
          });
          
          return {
            symbol: results[0][0],
            decimals: results[1][0],
            totalSupply: results[2][0],
          };
        },
        enabled: !!tokenAddress,
        staleTime: 60000, // Cache for 1 minute
      });
    };
    // ✅ retry configuration
    const { data, error } = useCallClause({
      queryOptions: {
        retry: (failureCount, error) => {
          if (error.message.includes('reverted')) return false;
          if (error.message.includes('invalid')) return false;
          return failureCount < 3;
        },
        retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
      },
    });
    // ✅ Mock VeChain Kit hooks
    jest.mock('@vechain/vechain-kit', () => ({
      useWallet: () => ({
        account: { address: '0x123...' },
        isConnected: true,
      }),
      useCallClause: () => ({
        data: [BigInt('1000000000000000000')],
        isLoading: false,
        error: null,
      }),
    }));
    'use client';
    
    import { VeChainKitProvider } from "@vechain/vechain-kit";
    
    export function Providers({ children }) {
      return (
        <VeChainKitProvider>
          {children}
        </VeChainKitProvider>
      );
    }
    import dynamic from 'next/dynamic';
    
    const VeChainKitProvider = dynamic(
      async () => (await import('@vechain/vechain-kit')).VeChainKitProvider,
      { ssr: false }
    );
    
    export function Providers({ children }) {
      return (
        <VeChainKitProvider>
          {children}
        </VeChainKitProvider>
      );
    }
    'use client';
    
    import { VeChainKitProvider } from "@vechain/vechain-kit";
    
    export function VeChainKitProviderWrapper({ children }: { children: React.ReactNode }) {
      return (
        <VeChainKitProvider
          // Network Configuration
          network={{
            type: "test", // "main" | "test" | "solo"
          }}
          
          // UI Configuration
          darkMode={false}
          language="en"
          theme={{
              backgroundColor: 'black'
          }}
          
          // Login Modal UI Customization
          loginModalUI={{
            logo: '/your-logo.png',
            description: 'Welcome to our DApp',
          }}
          
          // Login Methods Configuration
          loginMethods={[
            { method: "vechain", gridColumn: 4 },
            { method: "dappkit", gridColumn: 4 },
          ]}
          
          // Sponsor transactions
          feeDelegation={{
              delegatorUrl: process.env.NEXT_PUBLIC_DELEGATOR_URL!,
          }}
          
          // Wallet Connection Configuration
          dappKit={{
            allowedWallets: ["veworld", "wallet-connect", "sync2"],
            walletConnectOptions: {
              projectId: process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID!,
              metadata: {
                name: "Your DApp Name",
                description: "Your DApp description visible in wallets",
                url: typeof window !== "undefined" ? window.location.origin : "",
                icons: ["https://your-domain.com/logo.png"],
              },
            },
          }}
        >
          {children}
        </VeChainKitProvider>
      );
    }
    network: {
      type: "main" | "test" | "solo" // Select mainnet or testnet or solo
    }
    feeDelegation: {
      delegatorUrl: string,           // Fee delegation service URL
    }
    loginMethods: [
      // Always available methods
      { method: "vechain", gridColumn: 4 },    // VeChain social login
      { method: "dappkit", gridColumn: 4 },    // VeChain wallets
      { method: "ecosystem", gridColumn: 4 },  // Ecosystem apps (Mugshot, Cleanify, Greencart, etc.)
      
      // Privy-dependent methods (require your own privy configuration)
      { method: "email", gridColumn: 2 },      // Email login
      { method: "passkey", gridColumn: 2 },    // Passkey authentication
      { method: "google", gridColumn: 4 },     // Google OAuth
      { method: "more", gridColumn: 2 },       // Additional Privy methods
    ]
    <VeChainKitProvider
      privy={{
        appId: "your-privy-app-id",
        clientId: "your-privy-client-id",
        // Additional Privy configuration
      }}
      loginMethods={[
        // Now you can use Privy-dependent methods
        { method: "email", gridColumn: 2 },
        { method: "google", gridColumn: 4 },
        { method: "passkey", gridColumn: 2 },
      ]}
    >
      {children}
    </VeChainKitProvider>
    <VeChainKitProvider
      loginMethods={[
        { method: "ecosystem", gridColumn: 4 }
      ]}
      ecosystemApps={{
        allowedApps: ["app-id-1", "app-id-2"], // Find app IDs in Privy dashboard
      }}
    >
      {children}
    </VeChainKitProvider>

    mail

    Physical mailing address

    V3X HQ

    notice

    Notice regarding this name

    This is a notice

    location

    Generic location (e.g. "Toronto, Canada")

    Breda, NL

    phone

    Phone number as an E.164 string

    +1 234 567 890

    url

    Website URL

    header

    Image URL to be used as a header/banner

    ipfs://dQw4w9WgXcQ

    display

    Preferred capitalization

    ENSIP-5

    Luc.eth

    avatar

    Avatar or logo (see Avatars)

    ENSIP-5

    ipfs://dQw4w9WgXcQ

    description

    Description of the name

    ENSIP-5

    DevRel @ ENS Labs

    keywords

    List of comma-separated keywords

    ENSIP-5

    person, ens

    email

    ENS documentation
    vetDomains

    Email address

    Types

    Return values

    account: Wallet

    The primary account being used. This will be either:

    • The smart account (if connected via Privy)

    • The wallet address (if connected via DappKit)

    smartAccount: SmartAccount

    Information about the user's smart account:

    • address: The smart account address

    • domain: Associated VeChain domain name

    • image: Generated avatar image

    • isDeployed: Whether the smart account is deployed; smart accounts can be deployed on demand to avoid spending money on non active users. Learn more about smart accounts .

    • isActive: Whether this is the currently active account

    • version: Smart account contract version

    When the user is connected with Privy account will always be equal to the smartAccount.

    connectedWallet: Wallet

    The currently connected wallet, regardless of connection method (can be both a Privy Embedded Wallet or a self custody Wallet connected trough VeWorld or Sync2):

    • address: Wallet address

    • domain: Associated VeChain domain name

    • image: Generated avatar image

    privyUser: User | null

    The Privy user object if connected via Privy, null otherwise

    connection: ConnectionState

    Current connection state information:

    • isConnected: Overall connection status

    • isLoading: Whether connection is in progress

    • isConnectedWithSocialLogin: Connected via Privy (no crossapp)

    • isConnectedWithDappKit: Connected via DappKit

    • isConnectedWithCrossApp: Connected via cross-app

    • isConnectedWithPrivy: Connected via Privy (social or cross-app)

    • isConnectedWithVeChain: Connected with VeChain cross-app

    • source: Connection source information

    • isInAppBrowser: Whether your app is running in VeWorld app browser

    • nodeUrl: Current node URL (if provided by you in the provider, otherwise default in use by VeChain Kit)

    • delegatorUrl: Fee delegation service URL setted by you in the provider

    • chainId: Current chain ID

    • network: Network type (mainnet/testnet/custom)

    disconnect(): Promise<void>

    This function terminates the current wallet connection, ensuring cleanup of connection details and triggering necessary event listeners for state updates.

    Connection Sources

    The hook supports three main connection types:

    1. Social Login (privy): Authentication via social providers with your own APP_ID

    2. Wallet Connection (wallet): Direct wallet connection via DappKit

    3. Cross-App (privy-cross-app): Connection through ecosystem integration

    Events

    The hook dispatches a wallet_disconnected event when the wallet is disconnected, which can be used to trigger UI updates in dependent components.

    You need to set this parameter in your VeChainKitProvider weapper.

    You need one per environmnet.

    To obtain one you can deploy your own custom solution or use vechain.energy.

    Option 1: create your own

    You can deploy this as a microservice (through lambda, cloudflare, etc.) or as an endpoint of your backend.

    Option 2: use vechain.energy

    GO TO TUTORIAL

    Add this address 0xD7B96cAC488fEE053daAf8dF74f306bBc237D3f5 (MAINNET) or 0x7C5114ef27a721Df187b32e4eD983BaB813B81Cb (TESTNET) in {YourProjectName/FeeDelegation/Configurations/Smart Contract to sponsor all transactions.

    Enable email notifications to let you know of low VTHO balance.

    What is Fee Delegation?

    Fee Delegation is a simple process that creates a magical user experience using blockchains without losing decentralized benefits.

    The user submitting a transaction requests a second signature from a gas payer.

    When the transaction is processed, the gas costs are taken from the gas payers balance instead of the user submitting the transaction.

    It creates a feeless and trustless solution for instant blockchain access.

    You can read more about it here: https://docs.vechain.org/thor/learn/fee-delegation (opens in a new tab)

    Why is it important?

    It stops users from instantly using an application. Users that need to pay for transactions are required to obtain a token from somewhere. Regular users do not know where to go and researching a place to buy and accessing a (de)centralized exchange is too much to ask for a user that wants to use your application.

    What does it allow?

    Users can open an application and interact with it instantly. An Application can submit transactions in the background and can no longer be be distinguished from regular(web2) alternatives. Fee delegation solves the biggest hurdle of user on - boarding to blockchain - applications without invalidating the web3 - principles.

    Want to learn more?

    You can learn more about the feature from docs.vechain.org on How to Integrate VIP-191 (opens in a new tab).

    What are the use-cases?

    Fee Delegation is mostly meant to be implemented to support end-user-experience, removing the need to purchase/collect gas tokens and remove need to pay for the applications activities.

    Other use-cases are instant interactions with blockchains from different backends or processes without introducing account management for gas tokens.

    vechain.energy

    Language and Currency Synchronization

    VeChainKit supports bidirectional synchronization of language and currency preferences between the kit and host applications. This allows changes made in either the kit's settings or the host app to be reflected in both places.

    Overview

    The bidirectional sync feature enables:

    • Kit → Host: When users change language or currency in VeChainKit settings, the host app can be notified and update accordingly

    • Host → Kit: When the host app changes language or currency, VeChainKit automatically reflects these changes

    Features

    Language Synchronization

    Language preferences are synchronized between:

    • VeChainKit's language settings (accessible via Account Modal → Settings → General → Language)

    • Host application's i18n instance

    • Browser localStorage (i18nextLng)

    Currency Synchronization

    Currency preferences are synchronized between:

    • VeChainKit's currency settings (accessible via Account Modal → Settings → General → Currency)

    • Host application's state

    • Browser localStorage (vechain_kit_currency)

    Usage

    Basic Setup

    The bidirectional sync is enabled automatically when you use VeChainKitProvider. You can optionally provide callbacks to be notified of changes:

    Reading Current Values

    Using Convenience Hooks

    The easiest way to access current language and currency values:

    Using useVeChainKitConfig Hook

    You can also access current values through the config hook:

    Changing Values from Host App

    You can change language or currency from your host app, and VeChainKit will automatically sync:

    Listening to Changes

    Use the optional callback props to react to changes made in VeChainKit:

    In your i18n.ts file be sure to check localstorage for setting the language, to avoid issues during refresh.

    API Reference

    VeChainKitProvider Props

    language?: string

    Initial language code (e.g., 'en', 'fr', 'de'). Defaults to 'en'.

    defaultCurrency?: CURRENCY

    Initial currency code ('usd', 'eur', or 'gbp'). Defaults to 'usd'.

    onLanguageChange?: (language: string) => void

    Optional callback fired when language is changed in VeChainKit settings.

    onCurrencyChange?: (currency: CURRENCY) => void

    Optional callback fired when currency is changed in VeChainKit settings.

    Hooks

    useCurrentLanguage()

    Returns the current language and a function to change it.

    Returns:

    • currentLanguage: string - Current language code

    • setLanguage: (language: string) => void - Function to change language

    useCurrentCurrency()

    Returns the current currency and a function to change it.

    Returns:

    • currentCurrency: CURRENCY - Current currency code

    • setCurrency: (currency: CURRENCY) => void - Function to change currency

    useVeChainKitConfig()

    Returns the full VeChainKit configuration including current language and currency.

    Returns:

    • currentLanguage: string - Current runtime language value

    • currentCurrency: CURRENCY - Current runtime currency value

    • setLanguage: (language: string) => void - Function to change language

    Storage

    Language and currency preferences are persisted in browser localStorage:

    • Language: i18nextLng (managed by react-i18next)

    • Currency: vechain_kit_currency

    Changes persist across page reloads and browser sessions.

    Notes

    • Use currentLanguage and currentCurrency from useVeChainKitConfig() to get the current runtime values.

    • Changes made in VeChainKit settings automatically update localStorage and trigger callbacks.

    • Changes made via setLanguage() or setCurrency()

    Send Transactions

    The useSendTransaction hook is mandatory if you use social login in your app, since it handles the social login transactions (that needs to be prepared and broadcasted differently from normal transactions). If you are not interested in social login features then you can avoid using useSendTransaction and useSignMessage and use the signer exported by the kit following the SDK guides for creating transactions or signing messages.

    This hook will take care of checking your connection type and handle the transaction submission between privy, cross-app and wallet connections.

    When implementing VeChain Kit it is mandatory to use this hook to send transaction.

    Use our pre built TransactionModal or TransactionToast components to show your users the progress and outcome of the transaction, or build your own UX and UI.

    You can build clauses with some of our available build functions, with our or with .

    If you want to interact directly with the user's smart account read the section.

    Important

    Ensuring data is pre-fetched before initiating a transaction is crucial to avoid browser pop-up blocking for users using social login, which can adversely affect user experience.


    🔧 Custom Gas Configuration (optional)

    When sending transactions using VechainKit, you can fine-tune the gas settings by using two optional fields: suggestedMaxGas and gasPadding. These options give you greater control over the gas behavior of your transaction.

    suggestedMaxGas

    The suggestedMaxGas parameter allows you to explicitly define the maximum amount of gas to be used for the transaction. When this field is set, it will override the internal gas estimation logic and also ignore any gasPadding value.

    • Expected format: an integer representing gas units (e.g., 40000000 for 40 million gas).

    • Use this option when you want full control and know in advance the maximum gas your transaction should consume.

    Example:

    gasPadding

    The gasPadding option allows you to add a safety buffer on top of the estimated gas. This can be useful to prevent underestimations for complex transactions.

    • Expected format: a number between 0 and 1, representing a percentage increase (e.g., 0.1 adds 10%).

    • Only applied if suggestedMaxGas is not set.

    Example:

    Summary

    Option
    Type
    Applies Gas Estimation
    Applies Padding
    Overwrites Estimation

    Use suggestedMaxGas when you want to define the gas cap directly, and gasPadding when you prefer to work with auto-estimation but want a bit of headroom.

    Login Customization

    This guide covers all available authentication methods in VeChain Kit. Choose the approach that best fits your application's needs.

    Overview

    VeChain Kit provides four main authentication approaches:

    1. Pre-built WalletButton Component - Fastest implementation with automatic state management

    Wallet Button

    This button acts both as a login button and as an account button (when the user is already logged).

    VeChain Kit provides multiple ways to customize the UI components. Here are some examples of different button styles and variants.

    Usage example

    Removed Features in v2

    This document lists all hooks, modules, and features that have been completely removed in v2. If you use any of these, you'll need to find alternatives or implement custom solutions.

    Core Hooks Removed

    View removed core hooks

    Best Practices

    Essential patterns for optimal performance, type safety, and maintainability

    Type Safety

    Query Optimization

    export type Wallet = {
        address: string;
        domain?: string;
        image: string;
    } | null;
    export type SmartAccount = Wallet & {
        isDeployed: boolean;
        isActive: boolean;
        version: string | null;
    };
    export type ConnectionSource = {
        type: 'privy' | 'wallet' | 'privy-cross-app';
        displayName: string;
    };
    interface User {
        /** The Privy-issued DID for the user. If you need to store additional information
         * about a user, you can use this DID to reference them. */
        id: string;
        /** The datetime of when the user was created. */
        createdAt: Date;
        /** The user's email address, if they have linked one. It cannot be linked to another user. */
        email?: Email;
        /** The user's phone number, if they have linked one. It cannot be linked to another user. */
        phone?: Phone;
        /** The user's most recently linked wallet, if they have linked at least one wallet.
         *  It cannot be linked to another user.
         *  This wallet is the wallet that will be used for transactions and signing if it is connected.
         **/
        wallet?: Wallet;
        /**
         * The user's smart wallet, if they have set up through the Privy Smart Wallet SDK.
         */
        smartWallet?: SmartWallet;
        /** The user's Google account, if they have linked one. It cannot be linked to another user. */
        google?: Google;
        /** The user's Twitter account, if they have linked one. It cannot be linked to another user. */
        twitter?: Twitter;
        /** The user's Discord account, if they have linked one. It cannot be linked to another user. */
        discord?: Discord;
        /** The user's Github account, if they have linked one. It cannot be linked to another user. */
        github?: Github;
        /** The user's Spotify account, if they have linked one. It cannot be linked to another user. */
        spotify?: Spotify;
        /** The user's Instagram account, if they have linked one. It cannot be linked to another user. */
        instagram?: Instagram;
        /** The user's Tiktok account, if they have linked one. It cannot be linked to another user. */
        tiktok?: Tiktok;
        /** The user's LinkedIn account, if they have linked one. It cannot be linked to another user. */
        linkedin?: LinkedIn;
        /** The user's Apple account, if they have linked one. It cannot be linked to another user. */
        apple?: Apple;
        /** The user's Farcaster account, if they have linked one. It cannot be linked to another user. */
        farcaster?: Farcaster;
        /** The user's Telegram account, if they have linked one. It cannot be linked to another user. */
        telegram?: Telegram;
        /** The list of accounts associated with this user. Each account contains additional metadata
         * that may be helpful for advanced use cases. */
        linkedAccounts: Array<LinkedAccountWithMetadata>;
        /** The list of MFA Methods associated with this user. */
        mfaMethods: Array<MfaMethod>;
        /**
         * Whether or not the user has explicitly accepted the Terms and Conditions
         * and/or Privacy Policy
         */
        hasAcceptedTerms: boolean;
        /** Whether or not the user is a guest */
        isGuest: boolean;
        /** Custom metadata field for a given user account */
        customMetadata?: CustomMetadataType;
    }
    import { useWallet } from "@vechain/vechain-kit"; 
    
    function MyComponent = () => {
        const {
            account,
            connectedWallet,
            smartAccount,
            privyUser,
            connection,
            disconnect
        } = useWallet();
        
        return <></>
    }
    
    
    /// Smart contract to allow delegate all requests
    
    /// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    
    contract FeeDelegation {
        /**
         * @dev Check if a transaction can be sponsored for a user
         */
        function canSponsorTransactionFor(
            address _origin,
            address _to,
            bytes calldata _data
        ) public view returns (bool) {
            return true;
        }
    }
    import { Address, HDKey, Transaction, Secp256k1, Hex } from '@vechain/sdk-core';
    
    // the default signer is a solo node seeded account
    const DEFAULT_SIGNER = 'denial kitchen pet squirrel other broom bar gas better priority spoil cross'
    
    export async function onRequestPost({ request, env }): Promise<Response> {
        const body = await request.json()
        console.log('Incoming request', body);
    
        const signerWallet = HDKey.fromMnemonic((env.SIGNER_MNEMONIC ?? DEFAULT_SIGNER).split(' '), HDKey.VET_DERIVATION_PATH).deriveChild(0);
        if (!signerWallet.publicKey || !signerWallet.privateKey) { throw new Error('Could not load signing wallet') }
    
        const signerAddress = Address.ofPublicKey(signerWallet.publicKey)
        const transactionToSign = Transaction.decode(
            Buffer.from(body.raw.slice(2), 'hex'),
            false
        );
        const transactionHash = transactionToSign.getSignatureHash(Address.of(body.origin))
        const signature = Secp256k1.sign(transactionHash.bytes, signerWallet.privateKey)
    
        return new Response(JSON.stringify({
            signature: Hex.of(signature).toString(),
            address: signerAddress.toString()
        }), {
            status: 200,
            headers: {
                'Content-Type': 'application/json',
                'access-control-allow-origin': '*'
            }
        })
    }
    here
    ENSIP-5
    [email protected]
    ENSIP-5
    ENSIP-5
    ENSIP-5
    ENSIP-5
    ENSIP-5
    https://ens.domains
    ENSIP-18
    setCurrency: (currency: CURRENCY) => void - Function to change currency
  • ... (other config properties)

  • from the host app automatically sync to VeChainKit.
  • The sync works bidirectionally - changes in either direction are reflected in both places.

  • suggestedMaxGas

    Integer

    ❌

    ❌

    ✅

    gasPadding

    Float (0–1)

    ✅

    ✅

    ❌

    SDK
    connex
    Smart Accounts

    Buttons

    Customize button styles for different button variants. All button configs are grouped under the buttons object:

    Secondary Buttons (applies to all vechainKitSecondary buttons):

    buttons: {
        secondaryButton: {
            bg: 'rgba(255, 255, 255, 0.1)', // Background color
            color: '#ffffff', // Text color
            border: '1px solid rgba(255, 255, 255, 0.2)', // Border (full CSS string)
        },
    }

    Primary Buttons (applies to all vechainKitPrimary buttons):

    buttons: {
        primaryButton: {
            bg: '#3182CE', // Background color
            color: '#ffffff', // Text color
            border: 'none', // Border (full CSS string)
        },
    }

    Tertiary Buttons (applies to all vechainKitTertiary buttons):

    buttons: {
        tertiaryButton: {
            bg: 'transparent', // Background color
            color: '#ffffff', // Text color
            border: 'none', // Border (full CSS string)
        },
    }

    Login Buttons (applies to loginIn variant):

    You can customize multiple button types in one config:

    Custom Authentication UI - Full control over design and user experience

  • OAuth Social Login - Integrate with social providers (Privy self-hosted only)

  • Wallet-Only Connection - Direct wallet connection without social options

  • Method 1: Using the WalletButton Component

    The simplest way to add authentication is using the pre-built WalletButton component. It automatically handles:

    • Connection state management

    • Login/logout button switching

    • User profile display when connected

    For styling and customization options, see the WalletButton documentation.

    Method 2: Building Custom Authentication UI

    For complete control over the authentication experience, create a custom implementation using the provided hooks:

    Method 3: OAuth Social Login (Self-Hosted Privy Only)

    For applications using self-hosted Privy, you can implement OAuth authentication with popular social providers:

    Supported OAuth Providers

    • Google

    • Twitter (X)

    • Apple

    • Discord

    • GitHub

    • LinkedIn

    For detailed OAuth configuration, see the Login Hooks documentation.

    Method 4: Wallet-Only Connection

    To restrict authentication to wallet connections only (bypassing social login options):

    This method is useful when:

    • Your app requires only crypto wallet authentication

    • You want to bypass social login options

    • You need a streamlined wallet-focused experience

    Special Considerations

    VeWorld Mobile Integration

    When your application is accessed through the VeWorld mobile wallet browser, VeWorld is automatically enforced as the primary authentication method. This ensures seamless integration with the mobile wallet experience.

    Authentication State Management

    All authentication methods automatically sync with the VeChain Kit provider, ensuring consistent state across your application. You can access the current authentication state using:

    Error Handling

    Always implement proper error handling for authentication flows:

    Next Steps

    • Customize the WalletButton appearance

    • Learn about authentication hooks

    • Handle wallet interactions

    • Implement transaction signing

    Data Transformation

    Error Handling

    Query Key Management

    Performance Tips

    Security

    Single Contract Call Pattern

    Multiple Contract Calls Pattern

    Transaction Building Pattern

    Multi-Clause Transactions

    Error Handling

    import { VeChainKitProvider } from '@vechain/vechain-kit';
    
    function App() {
        const handleLanguageChange = (language: string) => {
            console.log('Language changed to:', language);
            // Update your app's language state
        };
    
        const handleCurrencyChange = (currency: 'usd' | 'eur' | 'gbp') => {
            console.log('Currency changed to:', currency);
            // Update your app's currency state
        };
    
        return (
            <VeChainKitProvider
                language="en"
                defaultCurrency="usd"
                onLanguageChange={handleLanguageChange}
                onCurrencyChange={handleCurrencyChange}
                // ... other props
            >
                {/* Your app */}
            </VeChainKitProvider>
        );
    }
    import { useCurrentLanguage, useCurrentCurrency } from '@vechain/vechain-kit';
    
    function MyComponent() {
        const { currentLanguage, setLanguage } = useCurrentLanguage();
        const { currentCurrency, setCurrency } = useCurrentCurrency();
    
        return (
            <div>
                <p>Current language: {currentLanguage}</p>
                <p>Current currency: {currentCurrency}</p>
    
                <button onClick={() => setLanguage('fr')}>Change to French</button>
    
                <button onClick={() => setCurrency('eur')}>Change to EUR</button>
            </div>
        );
    }
    import { useVeChainKitConfig } from '@vechain/vechain-kit';
    
    function MyComponent() {
        const config = useVeChainKitConfig();
    
        // Current runtime values
        const currentLanguage = config.currentLanguage; // 'fr' (current value)
        const currentCurrency = config.currentCurrency; // 'eur' (current value)
    
        // Functions to change values from host app
        config.setLanguage('de');
        config.setCurrency('gbp');
    }
    import { useCurrentLanguage, useCurrentCurrency } from '@vechain/vechain-kit';
    import { useTranslation } from 'react-i18next';
    
    function LanguageSelector() {
        const { currentLanguage, setLanguage } = useCurrentLanguage();
        const { i18n } = useTranslation();
    
        const handleLanguageChange = (newLang: string) => {
            // Update VeChainKit
            setLanguage(newLang);
            // Your i18n instance will also be updated automatically
        };
    
        return (
            <select
                value={currentLanguage}
                onChange={(e) => handleLanguageChange(e.target.value)}
            >
                <option value="en">English</option>
                <option value="fr">Français</option>
                <option value="de">Deutsch</option>
            </select>
        );
    }
    import { useState, useEffect } from 'react';
    import { VeChainKitProvider } from '@vechain/vechain-kit';
    
    function App() {
        const [appLanguage, setAppLanguage] = useState('en');
        const [appCurrency, setAppCurrency] = useState('usd');
    
        // Sync VeChainKit language changes to your app
        const handleLanguageChange = (language: string) => {
            setAppLanguage(language);
            // Update your app's i18n, routing, etc.
            console.log('Language changed in VeChainKit:', language);
        };
    
        // Sync VeChainKit currency changes to your app
        const handleCurrencyChange = (currency: 'usd' | 'eur' | 'gbp') => {
            setAppCurrency(currency);
            // Update your app's currency display, API calls, etc.
            console.log('Currency changed in VeChainKit:', currency);
        };
    
        return (
            <VeChainKitProvider
                language={appLanguage}
                defaultCurrency={appCurrency}
                onLanguageChange={handleLanguageChange}
                onCurrencyChange={handleCurrencyChange}
                // ... other props
            >
                {/* Your app */}
            </VeChainKitProvider>
        );
    }
    // Custom language detector that checks localStorage first, then prop, then browser
    const customLanguageDetector = {
        name: 'customDetector',
        lookup: (options?: { languages?: string[] } | undefined) => {
            // Check localStorage first (for persistence across page refreshes)
            if (typeof window !== 'undefined') {
                const storedLanguage = localStorage.getItem('i18nextLng');
                if (storedLanguage && supportedLanguages.includes(storedLanguage)) {
                    return storedLanguage;
                }
            }
    
            const propLanguage = options?.languages?.[0];
    
            if (propLanguage && supportedLanguages.includes(propLanguage)) {
                return propLanguage;
            }
    
            // Get browser language
            const browserLang = navigator.language.split('-')[0];
            if (browserLang && supportedLanguages.includes(browserLang)) {
                return browserLang;
            }
    
            return 'en'; // fallback
        },
        cacheUserLanguage: (lng: string) => {
            localStorage.setItem('i18nextLng', lng);
        },
    };
    'use client';
    
    import {
        useWallet,
        useSendTransaction,
        useTransactionModal,
        TransactionModal,
        getConfig
    } from '@vechain/vechain-kit';
    import { IB3TR__factory } from '@vechain/vechain-kit/contracts';
    import { humanAddress } from '@vechain/vechain-kit/utils';
    import { useMemo, useCallback } from 'react';
    
    export function TransactionExamples() {
        const { account } = useWallet();
        const b3trMainnetAddress = getConfig("main").b3trContractAddress;
        
        const clauses = useMemo(() => {
            const B3TRInterface = IB3TR__factory.createInterface();
    
            const clausesArray: any[] = [];
            clausesArray.push({
                to: b3trMainnetAddress,
                value: '0x0',
                data: B3TRInterface.encodeFunctionData('transfer', [
                    "0x0, // receiver address
                    '0', // 0 B3TR (in wei)
                ]),
                comment: `This is a dummy transaction to test the transaction modal. Confirm to transfer ${0} B3TR to ${humanAddress("Ox0")}`,
                abi: B3TRInterface.getFunction('transfer'),
            });
    
            return clausesArray;
        }, [connectedWallet?.address]);
    
        const {
            sendTransaction,
            status,
            txReceipt,
            resetStatus,
            isTransactionPending,
            error,
        } = useSendTransaction({
            signerAccountAddress: account?.address ?? '',
        });
    
        const {
            open: openTransactionModal,
            close: closeTransactionModal,
            isOpen: isTransactionModalOpen,
        } = useTransactionModal();
    
        // This is the function triggering the transaction and opening the modal
        const handleTransaction = useCallback(async () => {
            openTransactionModal();
            await sendTransaction(clauses);
        }, [sendTransaction, clauses, openTransactionModal]);
        
        const handleTryAgain = useCallback(async () => {
            resetStatus();
            await sendTransaction(clauses);
        }, [sendTransaction, clauses, resetStatus]);
    
        return (
            <>
                <button
                    onClick={handleTransactionWithModal}
                    isLoading={isTransactionPending}
                    isDisabled={isTransactionPending}
                >
                    Send B3TR
                </button>
    
                <TransactionModal
                    isOpen={isTransactionModalOpen}
                    onClose={closeTransactionModal}
                    status={status}
                    txReceipt={txReceipt}
                    txError={error}
                    onTryAgain={handleTryAgain}
                    uiConfig={{
                        title: 'Test Transaction',
                        description: `This is a dummy transaction to test the transaction modal. Confirm to transfer ${0} B3TR to ${
                            account?.address
                        }`,
                        showShareOnSocials: true,
                        showExplorerButton: true,
                        isClosable: true,
                    }}
                />
            </>
        );
    }
    
    // ✅ Good: Pre-fetch data
    const { data } = useQuery(['someData'], fetchSomeData);
    const sendTx = () => sendTransaction(data);
    
    // ⛔ Bad: Fetching data during the transaction
    const sendTx = async () => {
      const data = await fetchSomeData();
      return sendTransaction(data);
    };
    useSendTransaction({
      ..., //other config
      suggestedMaxGas: 40000000, // Sets the gas limit directly
    });
    useSendTransaction({
      ..., //other config
      gasPadding: 0.1, // Adds 10% buffer to estimated gas
    });
    'use client';
    
    import { VStack, Text, Button, Box, HStack, Grid } from '@chakra-ui/react';
    import {
        WalletButton,
        useAccountModal,
        ProfileCard,
        useWallet,
    } from '@vechain/vechain-kit';
    import { MdBrush } from 'react-icons/md';
    import { CollapsibleCard } from '../../ui/CollapsibleCard';
    
    export function UIControls() {
        const { open } = useAccountModal();
        const { account } = useWallet();
    
        return (
            <>
                <VStack spacing={6} align="stretch" w={'full'}>
                    <Text textAlign="center">
                        VeChain Kit provides multiple ways to customize the UI
                        components. Here are some examples of different button
                        styles and variants.
                    </Text>
    
                    <HStack w={'full'} justifyContent={'space-between'}>
                        {/* Mobile Variants */}
                        <HStack w={'full'} justifyContent={'center'}>
                            <VStack
                                w={'fit-content'}
                                spacing={6}
                                p={6}
                                borderRadius="md"
                                bg="whiteAlpha.50"
                            >
                                <Text fontWeight="bold">
                                    Account Button Variants
                                </Text>
                                <Text
                                    fontSize="sm"
                                    textAlign="center"
                                    color="gray.400"
                                >
                                    Note: Some variants might look different based
                                    on connection state and available data. Eg:
                                    "iconDomainAndAssets" will show the assets only
                                    if the user has assets. And same for domain
                                    name.
                                </Text>
                                <Grid
                                    templateColumns={{
                                        base: '1fr',
                                        md: 'repeat(2, 1fr)',
                                    }}
                                    gap={8}
                                    w="full"
                                    justifyContent="space-between"
                                >
                                    {/* First Column Items */}
                                    <VStack alignItems="flex-start" spacing={8}>
                                        <VStack alignItems="flex-start" spacing={2}>
                                            <Box w={'fit-content'}>
                                                <WalletButton
                                                    mobileVariant="icon"
                                                    desktopVariant="icon"
                                                />
                                            </Box>
                                            <Text
                                                fontSize="sm"
                                                fontWeight="medium"
                                                color="blue.300"
                                                bg="whiteAlpha.100"
                                                px={3}
                                                py={1}
                                                borderRadius="full"
                                            >
                                                variant: "icon"
                                            </Text>
                                        </VStack>
    
                                        <VStack alignItems="flex-start" spacing={2}>
                                            <Box w={'fit-content'}>
                                                <WalletButton
                                                    mobileVariant="iconAndDomain"
                                                    desktopVariant="iconAndDomain"
                                                />
                                            </Box>
                                            <Text
                                                fontSize="sm"
                                                fontWeight="medium"
                                                color="blue.300"
                                                bg="whiteAlpha.100"
                                                px={3}
                                                py={1}
                                                borderRadius="full"
                                            >
                                                variant: "iconAndDomain"
                                            </Text>
                                        </VStack>
    
                                        <VStack alignItems="flex-start" spacing={2}>
                                            <Box w={'fit-content'}>
                                                <WalletButton
                                                    mobileVariant="iconDomainAndAddress"
                                                    desktopVariant="iconDomainAndAddress"
                                                />
                                            </Box>
                                            <Text
                                                fontSize="sm"
                                                fontWeight="medium"
                                                color="blue.300"
                                                bg="whiteAlpha.100"
                                                px={3}
                                                py={1}
                                                borderRadius="full"
                                            >
                                                variant: "iconDomainAndAddress"
                                            </Text>
                                        </VStack>
                                    </VStack>
    
                                    {/* Second Column Items */}
                                    <VStack alignItems={'flex-start'} spacing={8}>
                                        <VStack alignItems="flex-start" spacing={2}>
                                            <Box w={'fit-content'}>
                                                <WalletButton
                                                    mobileVariant="iconDomainAndAssets"
                                                    desktopVariant="iconDomainAndAssets"
                                                />
                                            </Box>
                                            <Text
                                                fontSize="sm"
                                                fontWeight="medium"
                                                color="blue.300"
                                                bg="whiteAlpha.100"
                                                px={3}
                                                py={1}
                                                borderRadius="full"
                                            >
                                                variant: "iconDomainAndAssets"
                                            </Text>
                                        </VStack>
    
                                        <VStack alignItems="flex-start" spacing={2}>
                                            <Box w={'fit-content'}>
                                                <WalletButton
                                                    mobileVariant="iconDomainAndAssets"
                                                    desktopVariant="iconDomainAndAssets"
                                                    buttonStyle={{
                                                        border: '2px solid #000000',
                                                        boxShadow:
                                                            '-2px 2px 3px 1px #00000038',
                                                        background: '#f08098',
                                                        color: 'white',
                                                        _hover: {
                                                            background: '#db607a',
                                                            border: '1px solid #000000',
                                                            boxShadow:
                                                                '-3px 2px 3px 1px #00000038',
                                                        },
                                                        transition: 'all 0.2s ease',
                                                    }}
                                                />
                                            </Box>
                                            <Text
                                                fontSize="sm"
                                                fontWeight="medium"
                                                color="blue.300"
                                                bg="whiteAlpha.100"
                                                px={3}
                                                py={1}
                                                borderRadius="full"
                                            >
                                                variant: "iconDomainAndAssets"
                                                (styled)
                                            </Text>
                                        </VStack>
    
                                        <VStack alignItems="flex-start" spacing={2}>
                                            <Button onClick={open}>
                                                <Text>This is a custom button</Text>
                                            </Button>
                                            <Text
                                                fontSize="sm"
                                                fontWeight="medium"
                                                color="blue.300"
                                                bg="whiteAlpha.100"
                                                px={3}
                                                py={1}
                                                borderRadius="full"
                                            >
                                                no variant, custom button
                                            </Text>
                                        </VStack>
                                    </VStack>
                                </Grid>
                            </VStack>
                        </HStack>
                    </HStack>
                </VStack>
            </>
        );
    }
    
    buttons: {
        loginButton: {
            bg: 'transparent', // Background color
            color: '#ffffff', // Text color
            border: '1px solid rgba(255, 255, 255, 0.1)', // Border (full CSS string)
        },
    }
    buttons: {
        secondaryButton: {
            bg: 'rgba(255, 255, 255, 0.1)',
            color: '#ffffff',
            border: 'none',
        },
        primaryButton: {
            bg: '#3182CE',
            color: '#ffffff',
            border: 'none',
        },
        loginButton: {
            bg: 'transparent',
            color: '#ffffff',
            border: '1px solid rgba(255, 255, 255, 0.1)',
        },
    }
    'use client';
    
    import { WalletButton } from '@vechain/vechain-kit';
    
    export function Page() {
        return (
            <WalletButton />
        );
    }
    'use client';
    
    import { useConnectModal, useAccountModal, useWallet } from '@vechain/vechain-kit';
    
    export function CustomAuthButton() {
        const { connection } = useWallet();
        
        const { 
            open: openConnectModal, 
            close: closeConnectModal, 
            isOpen: isConnectModalOpen 
        } = useConnectModal();
        
        const { 
            open: openAccountModal, 
            close: closeAccountModal, 
            isOpen: isAccountModalOpen 
        } = useAccountModal();
        
        if (!connection.isConnected) {
            return (
                <button onClick={openConnectModal}>
                    Connect Wallet
                </button>
            );
        }
        
        return (
            <button onClick={openAccountModal}>
                View Account
            </button>
        );
    }
    import { useLoginWithOAuth } from '@vechain/vechain-kit';
    
    const SocialLoginComponent = () => {
        const { initOAuth } = useLoginWithOAuth();
    
        const handleOAuthLogin = async (provider: OAuthProvider) => {
            try {
                await initOAuth({ provider });
                console.log(`${provider} OAuth login initiated`);
            } catch (error) {
                console.error("OAuth login failed:", error);
            }
        };
    
        return (
            <div className="social-login-container">
                <h3>Sign in with:</h3>
                <div className="social-buttons">
                    <button onClick={() => handleOAuthLogin('google')}>
                        <GoogleIcon /> Google
                    </button>
                    <button onClick={() => handleOAuthLogin('twitter')}>
                        <TwitterIcon /> Twitter
                    </button>
                    <button onClick={() => handleOAuthLogin('apple')}>
                        <AppleIcon /> Apple
                    </button>
                    <button onClick={() => handleOAuthLogin('discord')}>
                        <DiscordIcon /> Discord
                    </button>
                </div>
            </div>
        );
    };
    
    export default SocialLoginComponent;
    import { useDAppKitWalletModal } from '@vechain/vechain-kit';
    
    export const WalletOnlyLogin = () => {
        const { open: openWalletModal } = useDAppKitWalletModal();
    
        return (
            <button onClick={openWalletModal}>
                Connect Wallet
            </button>
        );
    };
    import { useWallet } from '@vechain/vechain-kit';
    
    function Component() {
        const { connection, address, source } = useWallet();
        
        if (connection.isConnected) {
            console.log('Connected wallet:', address);
            console.log('Connection source:', source);
        }
    }
    const handleConnect = async () => {
        try {
            await openConnectModal();
        } catch (error) {
            console.error('Connection failed:', error);
            // Show user-friendly error message
        }
    };
    // ✅ Good: Proper typing
    const args: [string, bigint, boolean] = [address, amount, isEnabled];
    const contractAddress = config.contractAddress as `0x${string}`;
    const method = 'balanceOf' as const;
    
    // ✅ Use contract factories
    import { VOT3__factory } from '@vechain/vechain-kit/contracts';
    const abi = VOT3__factory.abi;
    
    // Avoid: Manual ABI definitions
    const functionAbi = contractAbi.find((e) => e.name === "delegates");
    // ✅ Good: Conditional enablement
    return useCallClause({
      abi,
      address: contractAddress,
      method: 'getData',
      args: [userAddress],
      queryOptions: {
        enabled: !!contractAddress && !!userAddress && isConnected,
      },
    });
    
    // ✅ Good: Configure caching
    queryOptions: {
      staleTime: 30000,        // 30 seconds for price data
      refetchInterval: 60000,  // Refetch every minute
    }
    // ✅ Good: Transform in select
    return useCallClause({
      abi: VOT3__factory.abi,
      address: contractAddress,
      method: 'convertedB3trOf' as const,
      args: [address ?? ''],
      queryOptions: {
        enabled: !!address,
        select: (data) => ({
          balance: ethers.formatEther(data[0]),
          formatted: humanNumber(ethers.formatEther(data[0])),
        }),
      },
    });
    
    // Avoid: Transform in component
    const transformedData = useMemo(() => ({
      balance: data?.[0]?.toString(),
    }), [data]); // Causes re-renders
    // ✅ Good: Comprehensive error handling
    if (error) {
      if (error.message.includes('reverted')) {
        return <div>Contract call failed. Check parameters.</div>;
      }
      if (error.message.includes('network')) {
        return <div>Network error. <button onClick={refetch}>Retry</button></div>;
      }
      return <div>Error: {error.message}</div>;
    }
    
    // ✅ Good: Retry logic
    queryOptions: {
      retry: (failureCount, error) => {
        if (error.message.includes('reverted')) return false;
        return failureCount < 3;
      },
      retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
    }
    // ✅ Good: Use getCallClauseQueryKey (without args) 
    export const getCurrentAllocationsRoundIdQueryKey = (
      address: string,
      networkType: NETWORK_TYPE
    ) =>
      getCallClauseQueryKey({
        abi: XAllocationVoting__factory.abi,
        address: getConfig(networkType).contractAddress as `0x${string}`,
        method: 'currentRoundId' as const,
      });
      
    // ✅ Good: Use getCallClauseQueryKeyWithArgs
    export const getTokenBalanceQueryKey = (
      address: string,
      networkType: NETWORK_TYPE
    ) =>
      getCallClauseQueryKeyWithArgs({
        abi: VOT3__factory.abi,
        address: getConfig(networkType).contractAddress as `0x${string}`,
        method: 'balanceOf' as const,
        args: [address],
      });
    
    // ✅ Good: Query invalidation after transactions
    const mutation = useBuildTransaction({
      clauseBuilder: buildClauses,
      onTxConfirmed: () => {
        queryClient.invalidateQueries({ queryKey: getTokenBalanceQueryKey(userAddress, networkType) });
      },
    });
    // ✅ Good: Memoize expensive calculations
    const queryKey = useMemo(() => 
      getTokenBalanceQueryKey(userAddress, tokenAddress),
      [userAddress, tokenAddress]
    );
    
    // ✅ Good: Batch multiple calls
    const results = await executeMultipleClausesCall({
      thor,
      calls: addresses.map((address) => ({
        abi: ERC20__factory.abi,
        functionName: 'balanceOf',
        address: address as `0x${string}`,
        args: [userAddress],
      })),
    });
    // ✅ Good: Input validation
    if (!isAddress(recipient)) {
      throw new Error('Invalid recipient address');
    }
    
    const amountBN = BigInt(amount);
    if (amountBN <= 0n) {
      throw new Error('Amount must be positive');
    }
    
    // ✅ Good: Safe BigInt handling
    const formatTokenAmount = (amount: bigint, decimals: number): string => {
      try {
        return ethers.formatUnits(amount, decimals);
      } catch (error) {
        return '0';
      }
    };
    import { VOT3__factory } from '@vechain/vechain-kit/contracts';
    import { useCallClause, getCallClauseQueryKeyWithArgs } from '@vechain/vechain-kit';
    
    const abi = VOT3__factory.abi;
    const method = 'convertedB3trOf' as const;
    
    export const useTokenBalance = (address?: string) => {
      const { network } = useVeChainKitConfig();
      const contractAddress = getConfig(network.type).contractAddress as `0x${string}`;
    
      return useCallClause({
        abi,
        address: contractAddress,
        method,
        args: [address ?? ''],
        queryOptions: {
          enabled: !!address,
          select: (data) => ({
            balance: ethers.formatEther(data[0]),
            formatted: humanNumber(ethers.formatEther(data[0])),
          }),
        },
      });
    };
    import { useQuery } from '@tanstack/react-query';
    import { executeMultipleClausesCall } from '@vechain/vechain-kit';
    
    export const useMultipleTokenData = (addresses: string[]) => {
      const thor = useThor();
    
      return useQuery({
        queryKey: ['MULTIPLE_TOKENS', addresses],
        queryFn: async () => {
          const results = await executeMultipleClausesCall({
            thor,
            calls: addresses.map((address) => ({
              abi: ERC20__factory.abi,
              functionName: 'balanceOf',
              address: address as `0x${string}`,
              args: [userAddress],
            })),
          });
    
          return addresses.map((address, index) => ({
            address,
            balance: ethers.formatEther(results[index][0]),
          }));
        },
        enabled: !!addresses.length,
      });
    };
    import { useBuildTransaction, useWallet } from '@vechain/vechain-kit';
    
    export const useTokenTransfer = () => {
      const { account } = useWallet();
      const thor = useThor();
    
      return useBuildTransaction({
        clauseBuilder: (recipient: string, amount: string) => {
          if (!account?.address) return [];
    
          const { clause } = thor.contracts
            .load(tokenAddress, ERC20__factory.abi)
            .clause.transfer(recipient, ethers.parseEther(amount));
    
          return [{
            ...clause,
            comment: `Transfer ${amount} tokens to ${recipient}`,
          }];
        },
        onTxConfirmed: () => {
          queryClient.invalidateQueries({ queryKey: ['TOKEN_BALANCE'] });
        },
      });
    };
    const useApproveAndSwap = () => {
      const { account } = useWallet();
      const thor = useThor();
    
      return useBuildTransaction({
        clauseBuilder: (tokenAddress: string, amount: string) => {
          if (!account?.address) return [];
    
          return [
            // Approve
            {
              ...thor.contracts
                .load(tokenAddress, ERC20__factory.abi)
                .clause.approve(swapAddress, ethers.parseEther(amount)).clause,
              comment: 'Approve token spending',
            },
            // Swap
            {
              ...thor.contracts
                .load(swapAddress, SwapContract__factory.abi)
                .clause.swap(tokenAddress, ethers.parseEther(amount)).clause,
              comment: 'Execute swap',
            },
          ];
        },
      });
    };
    const useContractCall = (address: string) => {
      return useCallClause({
        abi: ContractABI,
        address: address as `0x${string}`,
        method: 'getData',
        args: [],
        queryOptions: {
          enabled: !!address,
          retry: (failureCount, error) => {
            if (error.message.includes('reverted')) return false;
            return failureCount < 3;
          },
        },
      });
    };
    useConnex
    • Removed: useConnex hook

    • Replacement: Use useThor

    • Migration: See API Migration Guide

    useCall

    • Removed: Generic useCall hook

    • Replacement: Use useCallClause with proper typing

    • Migration: See API Migration Guide

    useEvents

    • Status: Completely refactored with new API

    • Note: Not removed, but has breaking API changes

    • Documentation: Blockchain Hooks

    VeBetterDAO Module (Completely Removed)

    The entire VeBetterDAO module has been removed. All hooks under api/vebetterdao/ are no longer available.

    View all removed VeBetterDAO hooks

    Galaxy Member Hooks

    • useGMbalance

    • useB3trToUpgrade

    • useB3trToUpgradeToLevel

    • useGetNodeIdAttached

    • useGetTokenIdAttachedToNode

    • useGMMaxLevel

    • useParticipatedInGovernance

    • useTokenIdByAccount

    • useNFTImage

    • useB3trDonated

    • useGMBaseUri

    • useSelectedTokenId

    • useIsGMClaimable

    • useSelectedGmNft

    • useLevelOfToken

    • useNFTMetadataUri

    Node Management Hooks

    • useGetNodeManager

    • useIsNodeHolder

    • useUserXNodes

    Rewards Hooks

    • useLevelMultiplier

    VePassport Hooks

    • useAccountLinking

    • usePassportChecks

    • useUserDelegation

    • useUserStatus

    X2Earn Rewards Pool Hooks

    • useUserVotesInAllRounds

    • useUserTopVotedApps

    XAllocation Pool Hooks

    • useAllocationAmount

    • useXAppVotesQf

    XApps Hooks

    • useXNode

    • useAppAdmin

    • useAppExists

    • useAppsEligibleInNextRound

    XNodes Hooks

    All XNodes-related functionality has been removed.

    Other Removed Modules

    View other removed modules

    Blockchain Module

    • getEvents utility removed

    • Use new event handling patterns instead

    ERC20 Module

    • useGetErc20Balance removed

    • Use useCallClause with ERC20 ABI instead

    Indexer Module

    • All indexer hooks removed

    • Implement custom indexing if needed

    Oracle Module

    • useGetTokenUsdPrice removed

    • Integrate with external price feeds directly

    veDelegate Module

    • useGetVeDelegateBalance removed

    • Use contract calls directly

    NFTs Module

    • All NFT-related hooks removed

    • Use generic contract interaction patterns

    Removed Utility Hooks

    View removed utility hooks

    useDecodeFunctionSignature

    • Purpose: Decoded function signatures from transaction data

    • Alternative: Use ethers.js or web3.js utilities directly

    useGetCustomTokenBalances

    • Purpose: Fetched balances for custom tokens

    • Alternative: Use useCallClause with token contracts

    useGetCustomTokenInfo

    • Purpose: Retrieved token metadata

    • Alternative: Query token contracts directly

    Removed Components

    View removed components

    ProfileCard

    • Purpose: component showing avatar, description, vet domain and address of the user

    • Alternative: Use the available hooks to build your own UI

    TransactionToast

    • Purpose: Show the status of a transaction in a toast component

    • Alternative: Use the receipt hook to track the status of the transaction and create your own UI or use the TransactionModal component

    Migration Strategies

    For VeBetterDAO Features

    If you depend on VeBetterDAO functionality:

    1. Option 1: Implement custom hooks using useCallClause

    2. Option 2: Wait for community implementations

    3. Option 3: Use direct contract interactions

    Example custom implementation:

    For Removed Utility Functions

    Most removed utilities can be replaced with:

    1. Direct contract calls using useCallClause

    2. Thor client methods

    3. External libraries (ethers.js, web3.js)

    Smart Accounts

    Our smart accounts are a simplified version of the Account Abstraction pattern, made for the VeChain blockchain, and are required in order to enable social login.

    Addresses

    Mainnet

    0xC06Ad8573022e2BE416CA89DA47E8c592971679A

    Testnet

    Every wallet on VeChain owns a smart account. The address of your smart account is deterministic, and it can be deployed at any time, and receive tokens even if it is not deployed yet.

    Testnet Factory address changed from v1 to v3 from 0x7EABA81B4F3741Ac381af7e025f3B6e0428F05Fb to 0x713b908Bcf77f3E00EFEf328E50b657a1A23AeaF which will cause all your testnet smart account addresses to change when upgrading to v1.5.0.

    Contracts

    There are 2 contracts that work together to enable social login and account abstraction:

    • SimpleAccount: A smart contract wallet owned by the user that can:

      • Execute transactions directly from the owner or through signed messages

      • Handle both single and batch transactions

    • SimpleAccountFactory: Factory contract that creates and manages SimpleAccount contracts:

    How it works

    1. Account Creation: When a user wants to create a smart account, they interact with the SimpleAccountFactory, which creates a new SimpleAccount instance with the user as the owner.

    2. Transaction Execution: The SimpleAccount can execute transactions in several ways:

      • Direct execution by the owner

    Version Management

    The system has evolved through multiple versions to improve functionality and security:

    • SimpleAccount:

      • V1: Basic account functionality with single transaction execution

      • V2: Skipped for misconfiguration during upgrade

      • V3: Introduced batch transactions with nonce-based replay protection, ownership transfer and version tracking

    The factory maintains compatibility with all account versions, ensuring a smooth experience across different dApps and versions.

    Testnet Factory address changed from v1 to v3 from 0x7EABA81B4F3741Ac381af7e025f3B6e0428F05Fb to 0x713b908Bcf77f3E00EFEf328E50b657a1A23AeaF which will cause all your testnet smart account addresses to change.

    Smart Accounts V3 upgrade

    Upgrading the user's smart accounts from V1 to V3 is mandatory, in order to protect against reply attacks and to allow multiclause transactions on VeChain.

    To facilitate the mandatory upgrade process, the kit now includes:

    • Built-in Check: Automatically displays an alert prompting users to upgrade if they possess a V1 smart account.

    • Hooks & Component: To avoid apps spending VTHO to upgrade accounts of users that won't do any action on the app we allow the developer to check upgradeability on demand by using the useUpgradeRequiredForAccount hook and show the UpgradeSmartAccountModal (importable from the kit).

    Hooks

    Developers can efficiently manage smart accounts using a provided by the kit.

    By importing these hooks, developers can:

    • Easily check if an upgrade is needed

    • Determine the current smart account version

    • Simplify maintaining and upgrading smart accounts

    This ensures users seamlessly benefit from the latest features and protections.

    Multiclause transactions

    Multiclause transactions offer enhanced security and flexibility within the VeChain ecosystem. Here's a breakdown of the key points for developers:

    • Single Clause Transactions: You can still use executeWithAuthorization, but keep in mind it retains a replay attack vector. This method is primarily for backward compatibility.

    • Recommended Adoption: Switch to executeBatchWithAuthorization for a more secure solution. This method:

      • Solves replay attack issues.

    Understanding these components is crucial for those working with smart accounts outside of the vechain-kit. This knowledge ensures both security and operational efficiency.

    How to use executeBatchWithAuthorization and the nonce :

    Currently VeChain Kit does not support the handling of a smart account by a VeWorld wallet.

    // Custom Galaxy Member balance hook
    const useGMBalance = (address: string) => {
      return useCallClause({
        abi: GalaxyMemberABI,
        address: GM_CONTRACT_ADDRESS,
        method: 'balanceOf',
        args: [address],
        queryOptions: {
          enabled: !!address
        }
      });
    };
  • useAppSecurityLevel

  • useGetCumulativeScoreWithDecay

  • useGetDelegatee

  • useGetDelegator

  • useGetEntitiesLinkedToPassport

  • useGetPassportForEntity

  • useGetPendingDelegationsDelegateePOV

  • useGetPendingDelegationsDelegatorPOV

  • useGetPendingLinkings

  • useIsEntity

  • useIsPassportCheckEnabled

  • useIsPassport

  • useParticipationScoreThreshold

  • useSecurityMultiplier

  • useThresholdParticipationScore

  • useThresholdParticipationScoreAtTimepoint

  • useIsBlacklisted

  • useIsWhitelisted

  • useUserRoundScore

  • useGetX2EarnAppAvailableFunds

  • useXAppsMetadataBaseUri

  • useXNodeCheckCooldown

  • Creates new accounts with deterministic addresses using CREATE2

  • Get the account address of a smart account without deploying it

  • Supports multiple accounts per owner through custom salts

  • Manages different versions of the SimpleAccount implementation

  • Batch execution of multiple transactions

  • Signature-based execution (useful for social login) - Deprecated, should avoid using this

  • Batch signature-based execution with replay protection (useful for social login + multiclause)

  • Batch signature-based execution with 16-bit chain ID to allow iOS and Android developers handle VeChain chain ID.

  • Nonce Management: For batch transactions with authorization (executeBatchWithAuthorization), a nonce is required to protect users against replay attacks:

    • The nonce should be generated when requesting the signature

    • Best practice is to use Date.now() as the nonce value

    • Each nonce can only be used once per account

    • Without proper nonce management, malicious actors could replay the same signed transaction multiple times

    • Nonces are only used and required for executeBatchWithAuthorization method

  • Social Login Integration: This system enables social login by creating deterministic account addresses for each user and allowing transactions to be signed off-chain and executed by anyone. This creates a seamless experience where users can interact with dApps using their social credentials.

  • SimpleAccountFactory:

    • V1: Basic account creation and management

    • V2: Added support for multiple accounts per owner using custom salts

    • V3: Support for V3 SimpleAccounts, enhanced version management and backward compatibility with legacy accounts

    Executes multiple transactions with a single signature, simulating multiclause capabilities.

  • Automatic Management: Using useSendTransaction from the kit automates this process, negating the need for manual adjustments.

  • 0x713b908Bcf77f3E00EFEf328E50b657a1A23AeaF
    variety of hooks
    Example of the upgrade modal
    const executeBatchWithAuthorization = (txClauses: Connex.VM.Clause[]) => {
        const toArray: string[] = [];
        const valueArray: string[] = [];
        const dataArray: string[] = [];
        
        txClauses.forEach((clause) => {
            toArray.push(clause.to ?? '');
            valueArray.push(String(clause.value));
            if (typeof clause.data === 'object' && 'abi' in clause.data) {
                dataArray.push(encodeFunctionData(clause.data));
            } else {
                dataArray.push(clause.data || '0x');
            }
        });
            
        const dataToSign = {
            domain: {
                name: 'Wallet',
                version: '1',
                chainId,
                verifyingContract, // the smart account of the user
            },
            types: {
                ExecuteBatchWithAuthorization: [
                    { name: 'to', type: 'address[]' },
                    { name: 'value', type: 'uint256[]' },
                    { name: 'data', type: 'bytes[]' },
                    { name: 'validAfter', type: 'uint256' },
                    { name: 'validBefore', type: 'uint256' },
                    { name: 'nonce', type: 'bytes32' },
                ],
                EIP712Domain: [
                    { name: 'name', type: 'string' },
                    { name: 'version', type: 'string' },
                    { name: 'chainId', type: 'uint256' },
                    { name: 'verifyingContract', type: 'address' },
                ],
            },
            primaryType: 'ExecuteBatchWithAuthorization',
            message: {
                to: toArray,
                value: valueArray,
                data: dataArray,
                validAfter: 0,
                validBefore: Math.floor(Date.now() / 1000) + 60, // e.g. 1 minute from now
                nonce: ethers.hexlify(ethers.randomBytes(32)), // nonce needs to be random
            },
        };
            
        // Sign the typed data with Privy
        const signature = (
          await signTypedDataPrivy(typedData, {
              uiOptions: {
                  title,
                  description,
                  buttonText,
              },
          })
        ).signature;
        
          
        // Rebuild the clauses to call the executeBatchWithAuthorization 
        const clauses = [];
        clauses.push(
            Clause.callFunction(
                Address.of(smartAccount.address),
                ABIContract.ofAbi(SimpleAccountABI).getFunction(
                    'executeBatchWithAuthorization',
                ),
                [
                    typedData.message.to,
                    typedData.message.value?.map((val) => BigInt(val)) ?? 0,
                    typedData.message.data,
                    BigInt(typedData.message.validAfter),
                    BigInt(typedData.message.validBefore),
                    typedData.message.nonce, // If your contract expects bytes32
                    signature as `0x${string}`,
                ],
            ),
        );
                       
        //Now you can broadcast the transaction to the blockchain with one of your delegator wallets
        // ...
    };

    API Migration Guide

    Core API changes when migrating from VeChain Kit 1.x to 2.0 with practical examples

    Update Dependencies

    First, update your package dependencies:

    Clean install to avoid conflicts:

    Update Imports

    Update your import statements throughout your codebase:

    Note: For the complete list of removed hooks,

    Connex to Thor

    Basic Setup

    v1:

    v2:

    Contract Interactions

    Reading Contract Data

    Single Contract Call

    v1:

    v2:

    Multiple Contract Calls

    v1:

    v2:

    Writing Contract Data (Transactions)

    Simple Transaction

    v1:

    v2:

    Multi-Clause Transaction

    v1:

    v2:

    Transaction Building

    Advanced Transaction Options

    v1:

    v2:

    Events Handling

    The events API has been redesigned in v2. .

    v1:

    v2:

    Query Keys

    Generating Query Keys

    v2 introduces specific query key functions:

    Custom Query Management

    Migration Tips

    1. Start with Reading Operations: Migrate useCall to useCallClause first

    2. Update Transactions Incrementally: Convert one transaction type at a time

    3. Test Thoroughly: The new patterns handle edge cases differently

    Fee Delegation Setup

    Fee delegation allows your dApp to sponsor transaction fees for users, removing the barrier of requiring VTHO tokens.

    What is Fee Delegation?

    Fee delegation is a VeChain feature that enables applications to pay transaction fees on behalf of users. This creates a seamless user experience, especially for:

    • New users without VTHO tokens

    Setup Fee Delegation Service – docs.vechain.energylearn.vechain.energy
    npm install @vechain/vechain-kit@^2.0.0
    npm uninstall @thor-devkit
    rm -rf node_modules package-lock.json
    npm install
    Logo
    Leverage Type Safety: Use TypeScript to catch migration issues
  • Use Query Keys: Implement proper cache management with new query key functions

  • see Removed Features
    See more
    // Before (1.x)
    import { useConnex, useWallet, useTransaction } from '@vechain/vechain-kit';
    
    // After (2.x)
    import { useThor, useWallet, useBuildTransaction, useCallClause } from '@vechain/vechain-kit';
    import { useConnex } from '@vechain/vechain-kit';
    
    const Component = () => {
      const connex = useConnex();
      
      // Access thor
      const thor = connex.thor;
      
      // Access vendor
      const vendor = connex.vendor;
    };
    import { useThor } from '@vechain/vechain-kit';
    
    const Component = () => {
      const thor = useThor();
      
      // Thor is now directly available
      // Vendor functionality is integrated into transaction methods
    };
    const getBalance = async () => {
      const functionAbi = contractAbi.find((e) => e.name === "balanceOf");
      const res = await thor.account(contractAddress)
        .method(functionAbi)
        .call(address);
      
      return ethers.formatEther(res.decoded[0]);
    };
    import { useCallClause } from '@vechain/vechain-kit';
    
    const useBalance = (address: string) => {
      const { data, isLoading, error } = useCallClause({
        abi: TokenContract__factory.abi,
        address: contractAddress as `0x${string}`,
        method: 'balanceOf' as const,
        args: [address],
        queryOptions: {
          enabled: !!address,
          select: (data) => ethers.formatEther(data[0]),
        },
      });
    
      return { balance: data, isLoading, error };
    };
    const fetchMultipleBalances = async (addresses: string[]) => {
      const results = [];
      for (const addr of addresses) {
        const res = await thor.account(contractAddress)
          .method(balanceOfAbi)
          .call(addr);
        results.push(res.decoded[0]);
      }
      return results;
    };
    import { executeMultipleClausesCall } from '@vechain/vechain-kit';
    
    const fetchMultipleBalances = async (addresses: string[]) => {
      const results = await executeMultipleClausesCall({
        thor,
        calls: addresses.map(addr => ({
          abi: TokenContract__factory.abi,
          functionName: 'balanceOf',
          address: contractAddress,
          args: [addr]
        }))
      });
      
      return results.map(r => r.result[0]);
    };
    const approve = async (spender: string, amount: string) => {
      const functionAbi = contractAbi.find((e) => e.name === "approve");
      const clause = thor.account(contractAddress)
        .method(functionAbi)
        .asClause(spender, amount);
      
      const tx = connex.vendor.sign('tx', [clause]);
      const result = await tx.request();
      
      return result.txid;
    };
    import { useBuildTransaction } from '@vechain/vechain-kit';
    
    const useApprove = () => {
      const {
        sendTransaction,
        status,
        txReceipt,
        isTransactionPending,
        error,
        resetStatus
      } = useBuildTransaction({
        clauseBuilder: (params: { spender: string; amount: string }) => {
          const { clause } = thor.contracts
            .load(contractAddress, TokenContract__factory.abi)
            .clause.approve(params.spender, params.amount);
          
          return [{
            ...clause,
            comment: 'Approve tokens'
          }];
        }
      });
    
      return {
        approve: sendTransaction,
        status,
        txReceipt,
        isTransactionPending,
        error,
        resetStatus
      };
    };
    const complexTransaction = async () => {
      const clauses = [
        thor.account(token1).method(approveAbi).asClause(spender, amount1),
        thor.account(token2).method(approveAbi).asClause(spender, amount2),
        thor.account(dex).method(swapAbi).asClause(token1, token2, amount1)
      ];
      
      const tx = connex.vendor.sign('tx', clauses);
      const result = await tx.request();
      
      return result;
    };
    const useComplexTransaction = () => {
      const { sendTransaction, status, txReceipt } = useBuildTransaction({
        clauseBuilder: (params) => {
          const clauses = [];
          
          // Approve token1
          const token1Contract = thor.contracts.load(token1, ERC20__factory.abi);
          clauses.push({
            ...token1Contract.clause.approve(params.spender, params.amount1).clause,
            comment: 'Approve token 1'
          });
          
          // Approve token2
          const token2Contract = thor.contracts.load(token2, ERC20__factory.abi);
          clauses.push({
            ...token2Contract.clause.approve(params.spender, params.amount2).clause,
            comment: 'Approve token 2'
          });
          
          // Perform swap
          const dexContract = thor.contracts.load(dex, DexABI);
          clauses.push({
            ...dexContract.clause.swap(token1, token2, params.amount1).clause,
            comment: 'Execute swap'
          });
          
          return clauses;
        }
      });
    
      return { sendTransaction, status, txReceipt };
    };
    const txWithOptions = async () => {
      const clause = thor.account(contractAddress)
        .method(methodAbi)
        .asClause(...args);
      
      const tx = connex.vendor.sign('tx', [clause])
        .signer(signerAddress)
        .gas(100000)
        .link('https://example.com/callback')
        .comment('Test transaction');
        
      return await tx.request();
    };
    const useTransactionWithOptions = () => {
        const { sendTransaction } = useBuildTransaction({
          clauseBuilder: (params) => {
            const { clause } = thor.contracts
              .load(contractAddress, ContractABI)
              .clause.methodName(...params.args);
    
            return [{
              ...clause,
              comment: 'My transaction'
            }];
          },
          suggestedMaxGas: 100000,
          gasPadding: 0.25 // 25% gas padding
        });
    
        return { sendTransaction };
    };
    const { events } = useEvents({
      contractAddress,
      eventName: 'Transfer',
      filters: { from: address }
    });
    // New events API - check documentation for updated usage
    import { useEvents } from '@vechain/vechain-kit';
    
    // The API has changed - refer to blockchain hooks documentation
    import { 
      getCallClauseQueryKeyWithArgs, 
      getCallClauseQueryKey 
    } from '@vechain/vechain-kit';
    
    // With arguments
    const queryKeyWithArgs = getCallClauseQueryKeyWithArgs({
      abi: ContractABI,
      address: contractAddress,
      method: 'balanceOf',
      args: [userAddress]
    });
    
    // Without arguments (for methods with no parameters)
    const queryKey = getCallClauseQueryKey({
      abi: ContractABI,
      address: contractAddress,
      method: 'totalSupply'
    });
    
    // Use with React Query for cache invalidation
    queryClient.invalidateQueries({ queryKey: queryKeyWithArgs });
    // Refresh specific contract data
    const refreshBalance = () => {
      const key = getCallClauseQueryKeyWithArgs({
        abi: TokenABI,
        address: tokenAddress,
        method: 'balanceOf',
        args: [account]
      });
      
      queryClient.invalidateQueries({ queryKey: key });
    };

    Social login users (email, Google, etc.)

  • Improving overall user onboarding

  • Configuration

    Add fee delegation to your VeChainKitProvider:

    Configuration Options

    • delegatorUrl: The endpoint URL for your fee delegation service

    • delegateAllTransactions:

      • true: Sponsor all transactions (wallet and social users)

      • false: Sponsor only social login transactions (mandatory)

    Setup Options

    You have two options for setting up fee delegation:

    Option 1: Create your own backend service

    Option 2: Use existing free tools

    Create Your Own Service

    Deploy a custom fee delegation service as a microservice or backend endpoint.

    Example Implementation (Cloudflare Worker)

    Environment Variables

    Security Considerations

    1. Never expose your mnemonic: Store it securely in environment variables

    2. Implement request validation: Check origin, transaction limits, etc.

    3. Add rate limiting: Prevent abuse of your delegation service

    4. Monitor usage: Track delegation requests and VTHO consumption

    Use VeChain.Energy

    VeChain.Energy provides a managed fee delegation service.

    Setup Steps

    For a detailed walkthrough, see the VeChain.Energy Fee Delegation Tutorial.

    1. Visit VeChain.Energy

      • Go to vechain.energy

      • Create an account and project

    2. Configure Fee Delegation

      • Navigate to: Your Project → Fee Delegation → Configurations

      • Add the VeChain Kit smart contract address:

        • Mainnet: 0xD7B96cAC488fEE053daAf8dF74f306bBc237D3f5

    3. Get Your Delegation URL

      • Your delegation URL will be: https://sponsor-testnet.vechain.energy/by/YOUR_PROJECT_ID

    4. Configure Your Provider

    Smart Contract for Custom Rules

    For advanced delegation rules, deploy a smart contract:

    Monitoring and Alerts

    For VeChain.Energy

    • Enable email notifications for low VTHO balance

    • Set up alerts in your project dashboard

    • Monitor usage statistics regularly

    For Custom Solutions

    Implement monitoring for:

    • VTHO balance of delegation wallet

    • Number of delegated transactions

    • Failed delegation attempts

    • Unusual activity patterns

    Testing Fee Delegation

    1. Test with Social Login

    2. Verify Delegation Headers. Check that your delegation service returns proper headers:

    Troubleshooting

    Common Issues

    1. "Fee delegation failed" error

      • Check delegation URL is correct

      • Verify VTHO balance in delegation wallet

      • Ensure CORS headers are properly set

    2. Social login users can't transact

      • Confirm feeDelegation is configured in provider

      • Check delegation service is running

      • Verify smart contract address is whitelisted

    3. High VTHO consumption

      • Implement transaction limits

      • Add user verification

      • Monitor for unusual patterns

    Best Practices

    1. Security First

      • Never expose private keys or mnemonics

      • Implement proper authentication

      • Add rate limiting

    2. User Experience

      • Ensure sufficient VTHO balance

      • Provide clear error messages

      • Monitor service uptime

    3. Cost Management

      • Set reasonable limits

      • Track usage per user

      • Implement fair use policies

    Sign Messages

    Sign Message

    Sign a string, eg "Hello VeChain", with the useSignMessage() hook.

    Sign Typed Data (EIP712)

    Use the useSignTypedData() hook to sign structured data.

    Certificate Signing

    To authenticate users in your backend (BE) and issue them a JWT (JSON Web Token), you may need to check that the connected user actually holds the private keys of that wallet (assuring he is not pretending to be someone else).

    The recommended approach is to use signTypedData to have the user sign a piece of structured data, ensuring the signed payload is unique for each authentication request.

    When using Privy the user owns a smart account (which is a smart contract), and he cannot directly sign a message with the smart account but needs to do it with his Embedded Wallet (the wallet created and secured by Privy). This means that when you verify identities of users connected with social login you will need to check that the address that signed the message is actually the owner of the smart account.

    Example usage

    Create a provider that will handle the signature verification.

    Wrap the app with our new provider

    Handle the signature within a custom hook

    On your backend validate the signature as follows:

    You should deprecate this:

    // Ensure social login users can transact without VTHO
    const { sendTransaction } = useWallet();
    
    try {
      const tx = await sendTransaction({
        to: '0x...',
        value: '0',
        data: '0x...'
      });
      console.log('Transaction sponsored:', tx);
    } catch (error) {
      console.error('Delegation failed:', error);
    }
    {
      "signature": "0x...",
      "address": "0x..."
    }
    <VeChainKitProvider
      feeDelegation={{
        delegatorUrl: "YOUR_FEE_DELEGATION_URL",
        delegateAllTransactions: true, // or false for social login only
      }}
    >
      {children}
    </VeChainKitProvider>
    import { 
      Address, 
      HDKey, 
      Transaction, 
      Secp256k1, 
      Hex 
    } from '@vechain/sdk-core';
    
    // Default signer for development (use env variable in production)
    const DEFAULT_SIGNER = 'denial kitchen pet squirrel other broom bar gas better priority spoil cross';
    
    export async function onRequestPost({ request, env }): Promise<Response> {
      try {
        const body = await request.json();
        console.log('Incoming fee delegation request:', body);
    
        // Load signer wallet from mnemonic
        const mnemonic = (env.SIGNER_MNEMONIC ?? DEFAULT_SIGNER).split(' ');
        const signerWallet = HDKey.fromMnemonic(
          mnemonic, 
          HDKey.VET_DERIVATION_PATH
        ).deriveChild(0);
        
        if (!signerWallet.publicKey || !signerWallet.privateKey) {
          throw new Error('Could not load signing wallet');
        }
    
        // Get signer address
        const signerAddress = Address.ofPublicKey(signerWallet.publicKey);
        
        // Decode and sign the transaction
        const transactionToSign = Transaction.decode(
          Buffer.from(body.raw.slice(2), 'hex'),
          false
        );
        
        const transactionHash = transactionToSign.getSignatureHash(
          Address.of(body.origin)
        );
        
        const signature = Secp256k1.sign(
          transactionHash.bytes, 
          signerWallet.privateKey
        );
    
        return new Response(JSON.stringify({
          signature: Hex.of(signature).toString(),
          address: signerAddress.toString()
        }), {
          status: 200,
          headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
          }
        });
      } catch (error) {
        console.error('Fee delegation error:', error);
        return new Response(JSON.stringify({ error: error.message }), {
          status: 500,
          headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
          }
        });
      }
    }
    # .env
    SIGNER_MNEMONIC="your twelve word mnemonic phrase here"
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    
    contract FeeDelegation {
        address public owner;
        mapping(address => bool) public whitelist;
        uint256 public dailyLimit = 1000; // VTHO limit
        mapping(address => uint256) public dailyUsage;
        mapping(address => uint256) public lastUsageDate;
        
        constructor() {
            owner = msg.sender;
        }
        
        modifier onlyOwner() {
            require(msg.sender == owner, "Only owner");
            _;
        }
        
        function addToWhitelist(address user) external onlyOwner {
            whitelist[user] = true;
        }
        
        function removeFromWhitelist(address user) external onlyOwner {
            whitelist[user] = false;
        }
        
        /**
         * @dev Check if a transaction can be sponsored
         */
        function canSponsorTransactionFor(
            address _origin,
            address _to,
            bytes calldata _data
        ) public view returns (bool) {
            // Always sponsor for whitelisted addresses
            if (whitelist[_origin]) {
                return true;
            }
            
            // Check daily limit for others
            uint256 today = block.timestamp / 86400;
            if (lastUsageDate[_origin] < today) {
                return true; // New day, can sponsor
            }
            
            return dailyUsage[_origin] < dailyLimit;
        }
        
        function updateUsage(address user, uint256 amount) external {
            uint256 today = block.timestamp / 86400;
            if (lastUsageDate[user] < today) {
                dailyUsage[user] = amount;
                lastUsageDate[user] = today;
            } else {
                dailyUsage[user] += amount;
            }
        }
    }
    // Example monitoring middleware
    async function monitorDelegation(request, response, next) {
      const vthoBalance = await getVTHOBalance(DELEGATION_ADDRESS);
      
      if (vthoBalance < MINIMUM_THRESHOLD) {
        await sendAlert('Low VTHO balance for fee delegation');
      }
      
      // Log delegation request
      await logDelegationRequest({
        origin: request.body.origin,
        timestamp: Date.now(),
        transactionHash: request.body.hash
      });
      
      next();
    }
    'use client';
    
    import { ReactElement, useCallback } from 'react';
    import {
        useSignMessage,
    } from '@vechain/vechain-kit';
    
    export function SigningExample(): ReactElement {
        const {
            signMessage,
            isSigningPending: isMessageSignPending,
            signature: messageSignature,
        } = useSignMessage();
    
       const handleSignMessage = useCallback(async () => {
            try {
                const signature = await signMessage('Hello VeChain!');
                toast({
                    title: 'Message signed!',
                    description: `Signature: ${signature.slice(0, 20)}...`,
                    status: 'success',
                    duration: 1000,
                    isClosable: true,
                });
            } catch (error) {
                toast({
                    title: 'Signing failed',
                    description:
                        error instanceof Error ? error.message : String(error),
                    status: 'error',
                    duration: 1000,
                    isClosable: true,
                });
            }
        }, [signMessage, toast]);
    
        return (
            <>
                <button
                    onClick={handleSignMessage}
                    isLoading={isMessageSignPending}
                >
                    Sign Typed Data
                </button>
                {typedDataSignature && (
                    <h4>
                        {typedDataSignature}
                    </h4>
                )}
            </>
        );
    }
    Testnet: 0x7C5114ef27a721Df187b32e4eD983BaB813B81Cb
    <VeChainKitProvider
      feeDelegation={{
        delegatorUrl: "https://sponsor-testnet.vechain.energy/by/YOUR_PROJECT_ID",
        delegateAllTransactions: false,
      }}
    >
      {children}
    </VeChainKitProvider>
    'use client';
    
    import { ReactElement, useCallback } from 'react';
    import {
        useWallet,
        useSignTypedData,
    } from '@vechain/vechain-kit';
    
    // Example EIP-712 typed data
    const exampleTypedData = {
        domain: {
            name: 'VeChain Example',
            version: '1',
            chainId: 1,
        },
        types: {
            Person: [
                { name: 'name', type: 'string' },
                { name: 'wallet', type: 'address' },
            ],
        },
        message: {
            name: 'Alice',
            wallet: '0x0000000000000000000000000000000000000000',
        },
        primaryType: 'Person',
    };
    
    export function SigningTypedDataExample(): ReactElement {
        const {
            signTypedData,
            isSigningPending: isTypedDataSignPending,
            signature: typedDataSignature,
        } = useSignTypedData();
        
        const { account } = useWallet()
    
        const handleSignTypedData = useCallback(async () => {
            try {
                const signature = await signTypedData(exampleTypedData, {
                    signer: account?.address
                });
                alert({
                    title: 'Typed data signed!',
                    description: `Signature: ${signature.slice(0, 20)}...`,
                    status: 'success',
                    duration: 1000,
                    isClosable: true,
                });
            } catch (error) {
                alert({
                    title: 'Signing failed',
                    description:
                        error instanceof Error ? error.message : String(error),
                    status: 'error',
                    duration: 1000,
                    isClosable: true,
                });
            }
        }, [signTypedData, toast, account]);
    
        return (
            <>
                <button
                    onClick={handleSignTypedData}
                    isLoading={isTypedDataSignPending}
                >
                    Sign Typed Data
                </button>
                {typedDataSignature && (
                    <h4>
                        {typedDataSignature}
                    </h4>
                )}
            </>
        );
    }
    import { useSignatureVerification } from "../hooks/useSignatureVerification";
    import { Modal, ModalOverlay, ModalContent, VStack, Text, Spinner, Button } from "@chakra-ui/react";
    import { useTranslation } from "react-i18next";
    import { ReactNode, useEffect } from "react";
    import { useWallet } from "@vechain/vechain-kit";
    import { clearSignature, usePostUser } from "@/hooks";
    
    type Props = {
      children: ReactNode;
    };
    
    export const SignatureVerificationWrapper = ({ children }: Props) => {
      const { hasVerified, isVerifying, signature, verifySignature, value } = useSignatureVerification();
      const { account } = useWallet();
      const { t } = useTranslation();
    
      const { mutate: postUser } = usePostUser();
    
      useEffect(() => {
        if (account?.address && !signature) {
          verifySignature();
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [account?.address, signature]);
    
      useEffect(() => {
        // If user signed the signature we call our backend endpoint
        if (signature && value.user !== "" && value.timestamp !== "" && account?.address) {
          // When you want to execute the mutation:
          postUser({
            address: account.address,
            value: value,
            signature: signature,
          });
        }
      }, [signature, value, account?.address, postUser]);
    
      // if user disconnects we clear the signature
      useEffect(() => {
        if (!account) {
          clearSignature();
        }
      }, [account]);
    
      // Only show the modal if we have an account connected AND haven't verified yet AND are not in the process of verifying
      const showModal = !!account?.address && (!hasVerified || !signature);
    
      return (
        <>
          {children}
          <Modal isOpen={showModal} onClose={() => {}} isCentered closeOnOverlayClick={false}>
            <ModalOverlay />
            <ModalContent p={6}>
              <VStack spacing={4}>
                {isVerifying ? (
                  <>
                    <Spinner size="xl" color="primary.500" />
                    <Text textAlign="center" fontSize="lg">
                      {t("Please sign the message to verify your wallet ownership")}
                    </Text>
                  </>
                ) : (
                  <>
                    <Text textAlign="center" fontSize="lg">
                      {t("Signature verification is mandatory to proceed. Please try again.")}
                    </Text>
                    <Button onClick={verifySignature} colorScheme="secondary">
                      {t("Try Again")}
                    </Button>
                  </>
                )}
              </VStack>
            </ModalContent>
          </Modal>
        </>
      );
    };
    import React from "react";
    import ReactDOM from "react-dom/client";
    import { VeChainKitProviderWrapper } from "./components/VeChainKitProviderWrapper.tsx";
    import { ChakraProvider, ColorModeScript } from "@chakra-ui/react";
    import { lightTheme } from "./theme";
    import { RouterProvider } from "react-router-dom";
    import { router } from "./router";
    import { AppProvider } from "./components/AppProvider.tsx";
    import { SignatureVerificationWrapper } from "./components/SignatureVerificationWrapper.tsx";
    
    (async () => {
      ReactDOM.createRoot(document.getElementById("root")!).render(
        <React.StrictMode>
          <ChakraProvider theme={lightTheme}>
            <ColorModeScript initialColorMode="light" />
            <VeChainKitProviderWrapper>
              <SignatureVerificationWrapper>
                  <AppProvider>
                    <RouterProvider router={router} />
                  </AppProvider>
              </SignatureVerificationWrapper>
            </VeChainKitProviderWrapper>
          </ChakraProvider>
        </React.StrictMode>,
      );
    })();
    
    import { useWallet, useSignTypedData } from "@vechain/vechain-kit";
    import { useState, useCallback } from "react";
    import { ethers } from "ethers";
    
    // EIP-712 typed data structure
    const domain = {
      name: "MyAppName",
      version: "1",
    };
    
    const types = {
      Authentication: [
        { name: "user", type: "address" },
        { name: "timestamp", type: "string" },
      ],
    };
    
    export type SignatureVerificationValue = {
      user: string;
      timestamp: string;
    };
    
    export const useSignatureVerification = () => {
      const { account } = useWallet();
      const { signTypedData } = useSignTypedData();
      const [isVerifying, setIsVerifying] = useState(false);
      const [value, setValue] = useState<SignatureVerificationValue>({
        user: "",
        timestamp: "",
      });
    
      const signature = localStorage.getItem(`login_signature`);
    
      const verifySignature = useCallback(async () => {
        if (!account?.address || isVerifying) return;
    
        setValue({
          user: account.address,
          timestamp: new Date().toISOString(),
        });
    
        const existingSignature = localStorage.getItem(`login_signature`);
        if (existingSignature) return;
    
        setIsVerifying(true);
        try {
          const signature = await signTypedData({
            domain,
            types,
            message: value,
            primaryType: "Authentication",
          }, {
            signer: account?.address
          });
    
          const isValid = ethers.verifyTypedData(domain, types, value, signature);
    
          if (!isValid) {
            throw new Error("Signature verification failed");
          }
    
          localStorage.setItem(`login_signature`, signature);
        } catch (error) {
          console.error("Signature verification failed:", error);
        } finally {
          setIsVerifying(false);
        }
      }, [account?.address, isVerifying, signTypedData, value]);
    
      const hasVerified = account?.address ? !!localStorage.getItem(`login_signature`) : false;
    
      return { hasVerified, isVerifying, signature, verifySignature, value };
    };
    import {ethers} from "ethers";
    
    static async verifySignature(signature: string, value: { user: string , timestamp: string }): Promise<string> {
        const domain = {
            name: "My App Name",
            version: "1",
        };
    
        const types = {
            Authentication: [
                {name: "user", type: "address"},
                {name: "timestamp", type: "string"},
            ],
        };
    
        return ethers.verifyTypedData(domain, types, value, signature);
    }
    
    const signerAddress = await verifySignature(
          signature,
          value
        );
        
    if (signerAddress.toLowerCase() != address.toLowerCase()) {
        return {
            statusCode: 403,
            body: JSON.stringify({
            error: "Invalid signature",
            }),
        };
    }
    
    import { useConnex } from '@vechain/vechain-kit';
    
    export default function Home(): ReactElement {
        const connex = useConnex();
    
        const handleSignWithConnex = () => {
            connex.vendor.sign(    
                'cert',
                {
                    purpose: 'identification',
                    payload: {
                        type: 'text',
                        content: 'Please sign this message to connect your wallet.',
                    },
                }
            ).request();
        }
        
        return <button onClick={handleSignWithConnex}> Sign </button>
    }

    Isolated Modals

    The hooks provide tools for managing various modals in the VeChain application:

    Use the { isolatedView: true } prop to show to not allow the user to browse other sections of the kit.

    Account Related Modals

    • useAccountModal: Core account modal management

    • useProfileModal: Show the user only his profile, with customize and logout option

    • useAccountCustomizationModal: Account customization settings

    • useAccessAndSecurityModal: Security settings and access management

    • useChooseNameModal: Account name selection

    • useUpgradeSmartAccountModal: Smart account upgrade management

    Wallet & Connection Modals

    • useConnectModal: Wallet connection modal

    • useWalletModal: Combined wallet management modal

    • useLoginModalContent: Login modal content configuration

    Transaction Modals

    • useTransactionModal: Transaction confirmation and status

    • useTransactionToast: Transaction notifications

    • useSendTokenModal: Token transfer interface

    Additional Feature Modals

    • useExploreEcosystemModal: VeChain ecosystem explorer

    • useNotificationsModal: Notification center

    • useFAQModal: Frequently asked questions

    Types

    Usage example

    useReceiveModal: Token receiving interface
    // Modal Types
    interface ModalHookReturn {
        open: () => void;
        close: () => void;
        isOpen: boolean;
    }
    
    type LoginMethod = 'ecosystem' | 'vechain' | 'dappkit' | 'passkey' | 'email' | 'google' | 'more';
    
    interface LoginModalContentConfig {
        showGoogleLogin: boolean;
        showEmailLogin: boolean;
        showPasskey: boolean;
        showVeChainLogin: boolean;
        showDappKit: boolean;
        showEcosystem: boolean;
        showMoreLogin: boolean;
        isOfficialVeChainApp: boolean;
    }
    
    interface AccountModalContent {
        type: string;
        props?: Record<string, any>;
    }
    // Example usage of Modal hooks
    import { 
        useWalletModal,
        useAccountModal,
        useSendTokenModal,
        useTransactionModal,
        useLoginModalContent
    } from '@modals';
    
    const ExampleComponent = () => {
        // Wallet modal management
        const { 
            open: openWallet,
            close: closeWallet,
            isOpen: isWalletOpen 
        } = useWalletModal();
    
        // Account modal management
        const {
            open: openAccount,
            close: closeAccount,
            isOpen: isAccountOpen
        } = useAccountModal();
    
        // Send token modal
        const {
            open: openSendToken,
            close: closeSendToken,
            isOpen: isSendTokenOpen
        } = useSendTokenModal();
    
        // Transaction modal
        const {
            open: openTransaction,
            close: closeTransaction,
            isOpen: isTransactionOpen
        } = useTransactionModal();
    
        // Login modal content configuration
        const loginConfig = useLoginModalContent();
    
        // Example handlers
        const handleWalletClick = () => {
            openWallet();
        };
    
        const handleSendToken = () => {
            openSendToken();
        };
    
        const handleAccountSettings = () => {
            openAccount();
        };
    
        return (
            <div>
                <button 
                    onClick={handleWalletClick}
                    disabled={isWalletOpen}
                >
                    Open Wallet
                </button>
    
                <button 
                    onClick={handleSendToken}
                    disabled={isSendTokenOpen}
                >
                    Send Tokens
                </button>
    
                <button 
                    onClick={handleAccountSettings}
                    disabled={isAccountOpen}
                >
                    Account Settings
                </button>
    
                {/* Login configuration display */}
                {loginConfig.showGoogleLogin && (
                    <button>Login with Google</button>
                )}
                {loginConfig.showVeChainLogin && (
                    <button>Login with VeChain</button>
                )}
                {loginConfig.showPasskey && (
                    <button>Login with Passkey</button>
                )}
    
                {/* Transaction modal state */}
                {isTransactionOpen && (
                    <div>Transaction in Progress...</div>
                )}
            </div>
        );
    };
    
    export default ExampleComponent;
    
    /*
    Note: These hooks require:
    - ModalProvider context
    - Valid wallet connection for some features
    - Proper configuration for login methods
    - Each modal manages its own state and content
    */