# 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.

<figure><img src="/files/yEWCeZ2RkLxaEsz8sECN" alt=""><figcaption></figcaption></figure>

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.

## 🚀 Start with AI

The fastest way to build with VeChain Kit is to hand a prompt to your coding agent (Claude Code, Cursor, or any agent). The prompts below tell the agent to read the relevant [VeChain AI Skill](https://github.com/vechain/vechain-ai-skills) first so it follows current conventions.

{% tabs %}
{% tab title="Start a new dApp" %}
{% code title="Paste into Claude Code, Cursor, or any AI agent" overflow="wrap" %}

```
Before doing anything, read these VeChain AI Skills so you follow current conventions:
- create-vechain-dapp: https://github.com/vechain/vechain-ai-skills/tree/main/skills/create-vechain-dapp
- vechain-kit: https://github.com/vechain/vechain-ai-skills/tree/main/skills/vechain-kit

Now the task:

Scaffold a new VeChain dApp for me using create-vechain-dapp, with:
- VeChain Kit pre-wired (Privy social login: Google + email, plus VeWorld and WalletConnect)
- Chakra UI v3 (with next-themes) and dark mode by default — follow the next-chakra-v3 example in the vechain-kit repo for wiring the kit's `theme` prop via `useChakraContext().token.var(...)` so theme tokens stay reactive
- A landing page that shows the connected user's address, B3TR balance, and a "Send B3TR" button
- A GitHub Pages deploy workflow ready to use

Name the project "my-vechain-dapp". When done, run `yarn dev` and tell me the URL.
```

{% endcode %}
{% endtab %}

{% tab title="Add to existing project" %}
{% code title="Paste into Claude Code, Cursor, or any AI agent" overflow="wrap" %}

```
Before doing anything, read this VeChain AI Skill so you follow current conventions:
- vechain-kit: https://github.com/vechain/vechain-ai-skills/tree/main/skills/vechain-kit

Now the task:

I already have a Next.js app and I want to add VeChain Kit to it.

1. Install @vechain/vechain-kit and any required peer deps.
2. Find my root layout (app/layout.tsx for App Router or pages/_app.tsx for Pages Router) and wrap it with VeChainKitProvider.
3. Enable Privy social login (Google + email), VeWorld and WalletConnect.
4. Read Privy keys from NEXT_PUBLIC_PRIVY_* and the WalletConnect projectId from NEXT_PUBLIC_WC_PROJECT_ID.
5. Add a <WalletButton /> to my existing header.
6. Don't change my existing Chakra theme.

If you hit peer-dependency conflicts, stop and tell me before applying any fix.
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Install the VeChain AI Skills once** to give every future prompt automatic domain context:

```bash
npx skills add vechain/vechain-ai-skills
```

Or in Claude Code: `/plugin marketplace add vechain/vechain-ai-skills`. Browse all 11 skills live at the [VeKit Playground](https://playground.vechainkit.vechain.org/ai-skills).
{% endhint %}

Prefer to wire it yourself? See the [manual installation guide](/quickstart/installation).

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>VeKit Playground</td><td><a href="/files/zn83vmfIV7MwNZCvapL7">/files/zn83vmfIV7MwNZCvapL7</a></td><td><a href="https://playground.vechainkit.vechain.org/">https://playground.vechainkit.vechain.org/</a></td></tr><tr><td>Homepage</td><td><a href="/files/zn83vmfIV7MwNZCvapL7">/files/zn83vmfIV7MwNZCvapL7</a></td><td><a href="https://vechainkit.vechain.org/">https://vechainkit.vechain.org/</a></td></tr><tr><td>NPM Package</td><td><a href="/files/mD4H5MAoSBwr8Y5ufrqn">/files/mD4H5MAoSBwr8Y5ufrqn</a></td><td><a href="https://www.npmjs.com/package/@vechain/vechain-kit">https://www.npmjs.com/package/@vechain/vechain-kit</a></td></tr><tr><td>Getting Started</td><td><a href="/files/BdVPcVx0YHV9oojvHqS7">/files/BdVPcVx0YHV9oojvHqS7</a></td><td><a href="/pages/cEqkWSfWI86VhmLx56xU">/pages/cEqkWSfWI86VhmLx56xU</a></td></tr><tr><td>Customization</td><td><a href="/files/KhCeR5zK43lzdTuD03P1">/files/KhCeR5zK43lzdTuD03P1</a></td><td><a href="/pages/U4VfGSXBXvRpBMtD2sSq">/pages/U4VfGSXBXvRpBMtD2sSq</a></td></tr><tr><td>Components</td><td><a href="/files/5RAmrT3FrIXLLSsjyk9M">/files/5RAmrT3FrIXLLSsjyk9M</a></td><td><a href="/pages/qAwfN6j3N4it8QpMhJyK">/pages/qAwfN6j3N4it8QpMhJyK</a></td></tr><tr><td>Troubleshooting</td><td><a href="/files/uzYdbWmw22v1FYm5Dxuh">/files/uzYdbWmw22v1FYm5Dxuh</a></td><td><a href="/pages/w35TVgE7Vr6Dx0TXjdEQ">/pages/w35TVgE7Vr6Dx0TXjdEQ</a></td></tr></tbody></table>


# 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.

### 🔑 Connect Modal v2.7 — custom UI for VeWorld / Sync2

The connect modal now owns the **entire VeWorld and Sync2 connection flow** end-to-end. No more hand-off to dapp-kit's native picker — clicking a wallet button drives `@vechain/dapp-kit` programmatically and renders the kit's own "Waiting for signature…" view. WalletConnect's QR modal is preserved.

Highlights:

* New granular `loginMethods` entries: `veworld`, `sync2`, `wallet-connect`, `apple`.
* New variation A layout — VeWorld primary filled (recommended), Google / Apple outline secondary, and a "More options ⌄" link footer that opens an in-modal sub-view with overflow wallets / socials / ecosystem apps.
* Themeable accent — `theme.accent` drives the spinner, focus rings and the "Waiting for signature…" headline.
* Zero breaking changes: pin `{ method: 'dappkit' }` in `loginMethods` to keep the legacy flow.

See [Login Customization](/quickstart/login-customization) for the full guide.

### ⚡ 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 [**`@vechain/vechain-contract-types`**](https://www.npmjs.com/package/@vechain/vechain-contract-types) and [**`@vechain/contract-getters`**](https://www.npmjs.com/package/@vechain/contract-getters), 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 [Customization](/customization/theming) 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 [Fee Delegation](/social-login/fee-delegation) 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
* DeFi integrations
* And more

{% hint style="danger" %}
**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 [the following section](https://github.com/vechain/vechain-kit/blob/feat/docs/docs/migrations/upgrade-vechain-kit-from-1.x-to-2.x).
{% endhint %}

{% hint style="warning" %}
**Version 1 Deprecation**\
Support for version 1 will cease, and updates will target version 2, encouraging users to migrate to leverage new features.
{% endhint %}


# 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](https://docs.vechain.org/developer-resources/sdks-and-providers/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.

### [DApp Kit](https://docs.vechain.org/developer-resources/sdks-and-providers/dapp-kit)

* 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.


# Configs

## VeChain Kit Configuration

The configuration system in VeChain Kit provides a comprehensive way to set up different network environments and contract addresses for VeChain applications.

### Network Types

Three network types are supported:

* `main` - VeChain mainnet
* `test` - VeChain testnet
* `solo` - Local development network

### Available data

```typescript
export type AppConfig = {
    ipfsFetchingService: string;
    ipfsPinningService: string;
    vthoContractAddress: string;
    b3trContractAddress: string;
    vot3ContractAddress: string;
    b3trGovernorAddress: string;
    timelockContractAddress: string;
    xAllocationPoolContractAddress: string;
    xAllocationVotingContractAddress: string;
    emissionsContractAddress: string;
    voterRewardsContractAddress: string;
    galaxyMemberContractAddress: string;
    treasuryContractAddress: string;
    x2EarnAppsContractAddress: string;
    x2EarnCreatorContractAddress: string;
    x2EarnRewardsPoolContractAddress: string;
    nodeManagementContractAddress: string;
    veBetterPassportContractAddress: string;
    veDelegate: string;
    veDelegateVotes: string;
    veDelegateTokenContractAddress: string;
    oracleContractAddress: string;
    accountFactoryAddress: string;
    cleanifyCampaignsContractAddress: string;
    cleanifyChallengesContractAddress: string;
    veWorldSubdomainClaimerContractAddress: string;
    vetDomainsContractAddress: string;
    vetDomainsPublicResolverAddress: string;
    vetDomainsReverseRegistrarAddress: string;
    vnsResolverAddress: string;
    vetDomainAvatarUrl: string;
    nodeUrl: string;
    indexerUrl: string;
    b3trIndexerUrl: string;
    graphQlIndexerUrl: string;
    network: Network;
    explorerUrl: string;
};
```

### Basic Usage

```typescript
// Basic Configuration Usage
import { getConfig, NETWORK_TYPE } from '@vechain-kit/core';

// Get config for specific network
const mainnetConfig = getConfig('main');
const testnetConfig = getConfig('test'); 
const soloConfig = getConfig('solo');

// Access network properties
console.log('Mainnet Node URL:', mainnetConfig.nodeUrl);
console.log('Testnet Explorer:', testnetConfig.explorerUrl);

// Access contract addresses
const {
    vthoContractAddress,
    accountFactoryAddress,
    oracleContractAddress,
    vetDomainsContractAddress
} = mainnetConfig;

// Access IPFS configuration
const {
    ipfsFetchingService,
    ipfsPinningService
} = mainnetConfig;

// Example: Create explorer link
const getExplorerUrl = (txId: string) => 
    `${mainnetConfig.explorerUrl}/${txId}`;
```

### Overriding Contract Addresses

If you deploy custom contract instances (e.g., on solo or testnet), you can override any config field via the `contractAddresses` prop on `VeChainKitProvider`:

```typescript
<VeChainKitProvider
  network={{ type: "solo" }}
  contractAddresses={{
    b3trContractAddress: "0x...",
    vot3ContractAddress: "0x...",
  }}
>
```

The overrides are merged with the base config for the selected network. Access the merged config in hooks/components with `useAppConfig`:

```typescript
import { useAppConfig } from '@vechain/vechain-kit';

const config = useAppConfig();
// config.b3trContractAddress — uses your override if provided, otherwise network default
```

`useAppConfig` is the recommended way to access contract addresses in your components, as it respects any overrides passed to the provider. `getConfig()` only returns the built-in defaults.

### Notes

* Network configurations are immutable after initialization
* Solo network is intended for local development
* Contract addresses vary between networks — and can be overridden via `contractAddresses`
* Explorer URLs are network-specific
* Genesis blocks define network identity
* Block time is standardized at 10 seconds
* IPFS services may have rate limits
* Some features may be testnet-only


# Utils

## VeChain Kit Utilities

A comprehensive collection of utility functions for VeChain development.

### Address Utilities

```typescript
import { 
    compareAddresses,
    compareListOfAddresses,
    isValidAddress,
    leftPadWithZeros
} from '@vechain-kit/utils';

// Compare two addresses (case-insensitive)
compareAddresses('0x123...', '0x123...'); // true

// Compare arrays of addresses
compareListOfAddresses(
    ['0x123...', '0x456...'], 
    ['0x456...', '0x123...']
); // true

// Validate address format
isValidAddress('0x123...'); // true/false

// Pad address with leading zeros
leftPadWithZeros('0x123', 40); // '0x0000...0123'
```

### Formatting Utilities

```typescript
import { 
    humanAddress,
    humanDomain,
    humanNumber,
    getPicassoImage
} from '@vechain-kit/utils';

// Format address for display
humanAddress('0x123456789...', 6, 4); // '0x1234••••89ab'

// Format domain for display
humanDomain('very.long.domain.vet', 8, 6); // 'very.lon••••in.vet'

// Format numbers with optional symbol
humanNumber('1000000', null, 'VET'); // '1,000,000 VET'
humanNumber('1000000', 2); // '1,000,000.00'

// Generate avatar image from address
getPicassoImage('0x123...', false); // SVG data URL
```

### Hex Utilities

```typescript
// Hex Utilities
import { 
    removePrefix,
    addPrefix,
    isValid,
    normalize,
    compare,
    generateRandom
} from '@vechain-kit/utils';

// Remove '0x' prefix
removePrefix('0x123'); // '123'

// Add '0x' prefix
addPrefix('123'); // '0x123'

// Validate hex string
isValid('0x123abc'); // true

// Normalize hex string (lowercase with prefix)
normalize('0X123ABC'); // '0x123abc'

// Compare hex strings
compare('0x123', '0X123'); // true

// Generate random hex
generateRandom(10); // '0x1234567890'
```

### IPFS Utilities

```typescript
// IPFS Utilities
import { 
    validateIpfsUri,
    toIPFSURL,
    uploadBlobToIPFS,
    ipfsHashToUrl
} from '@vechain-kit/utils';

// Validate IPFS URI
validateIpfsUri('ipfs://QmfSTia...'); // true

// Convert CID to IPFS URL
toIPFSURL('QmfSTia...', 'image.png'); // 'ipfs://QmfSTia.../image.png'

// Upload to IPFS
const hash = await uploadBlobToIPFS(blob, 'file.jpg', 'main');

// Convert IPFS hash to gateway URL
ipfsHashToUrl('QmfSTia...', 'main'); // https://gateway.ipfs.io/ipfs/QmfSTia...
```

### Media Type Resolver

```typescript
// Media Type Resolver
import { 
    resolveMediaTypeFromMimeType,
    NFTMediaType 
} from '@vechain-kit/utils';

// Determine NFT media type
const imageType = resolveMediaTypeFromMimeType('image/jpeg'); // NFTMediaType.IMAGE
const videoType = resolveMediaTypeFromMimeType('video/mp4'); // NFTMediaType.VIDEO
const audioType = resolveMediaTypeFromMimeType('audio/mp3'); // NFTMediaType.AUDIO
const modelType = resolveMediaTypeFromMimeType('model/gltf-binary'); // NFTMediaType.MODEL

// Available media types
console.log(NFTMediaType.IMAGE); // 'image'
console.log(NFTMediaType.VIDEO); // 'video'
console.log(NFTMediaType.AUDIO); // 'audio'
console.log(NFTMediaType.MODEL); // 'model'
```


# Installation

How to install and set up VeChain Kit in your project

### 🚀 Start with AI (recommended)

The fastest path: hand the prompt below to your coding agent (Claude Code, Cursor, or any agent). The agent will read the relevant [VeChain AI Skill](https://github.com/vechain/vechain-ai-skills) first, then scaffold or wire up the project for you.

{% tabs %}
{% tab title="Start a new dApp" %}
{% code title="Paste into your agent" overflow="wrap" %}

```
Before doing anything, read these VeChain AI Skills so you follow current conventions:
- create-vechain-dapp: https://github.com/vechain/vechain-ai-skills/tree/main/skills/create-vechain-dapp
- vechain-kit: https://github.com/vechain/vechain-ai-skills/tree/main/skills/vechain-kit

Now the task:

Scaffold a new VeChain dApp for me using create-vechain-dapp, with:
- VeChain Kit pre-wired (Privy social login: Google + email, plus VeWorld and WalletConnect)
- Chakra UI v3 (with next-themes) and dark mode by default — follow the next-chakra-v3 example in the vechain-kit repo for wiring the kit's `theme` prop via `useChakraContext().token.var(...)` so theme tokens stay reactive
- A landing page that shows the connected user's address, B3TR balance, and a "Send B3TR" button
- A GitHub Pages deploy workflow ready to use

Name the project "my-vechain-dapp". When done, run `yarn dev` and tell me the URL.
```

{% endcode %}
{% endtab %}

{% tab title="Add to existing project" %}
{% code title="Paste into your agent" overflow="wrap" %}

```
Before doing anything, read this VeChain AI Skill so you follow current conventions:
- vechain-kit: https://github.com/vechain/vechain-ai-skills/tree/main/skills/vechain-kit

Now the task:

I already have a Next.js app and I want to add VeChain Kit to it.

1. Install @vechain/vechain-kit and any required peer deps.
2. Find my root layout (app/layout.tsx for App Router or pages/_app.tsx for Pages Router) and wrap it with VeChainKitProvider.
3. Enable Privy social login (Google + email), VeWorld and WalletConnect.
4. Read Privy keys from NEXT_PUBLIC_PRIVY_* and the WalletConnect projectId from NEXT_PUBLIC_WC_PROJECT_ID.
5. Add a <WalletButton /> to my existing header.
6. Don't change my existing Chakra theme.

If you hit peer-dependency conflicts, stop and tell me before applying any fix.
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
Install the skills once to give every future prompt automatic context: `npx skills add vechain/vechain-ai-skills`. Try them live at the [VeKit Playground](https://playground.vechainkit.vechain.org/ai-skills).
{% endhint %}

### Get the template

Prefer a one-shot scaffold without an agent? The CLI has you covered.

```bash
$ npx create-vechain-dapp@latest

? Select template ›
    ❯ VeChain Kit Next.js Template (Chakra, React Query, SDK)
```

Or install the kit into your existing package as shown below.

### Quick-start (manual)

{% stepper %}
{% step %}

#### Install `@vechain/vechain-kit` and 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.

{% code fullWidth="true" expandable="true" %}

```shellscript
npm install --legacy-peer-deps @vechain/vechain-kit @chakra-ui/react@^2.8.2 \
  @emotion/react@^11.14.0 \
  @emotion/styled@^11.14.0 \
  @tanstack/react-query@^5.64.2 \
  @vechain/dapp-kit-react@2.1.0-rc.1 \
  framer-motion@^11.15.0
```

{% endcode %}
{% endstep %}

{% step %}

#### 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).

```typescript
'use client';

import { VeChainKitProvider } from "@vechain/vechain-kit";

export function Providers({ children }) {
  return (
    <VeChainKitProvider>
      {children}
    </VeChainKitProvider>
  );
}
```

{% endstep %}

{% step %}

#### Enjoy!

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

<pre class="language-typescript"><code class="lang-typescript"><strong>"use client";
</strong>import { WalletButton } from "@vechain/vechain-kit";


const Demo = () => {
  return (
    &#x3C;div>
       &#x3C;WalletButton /> {/* Login Button */}
       &#x3C;p>{account?.address}&#x3C;/p> {/* Address of the connected account */}
    &#x3C;/div>
  )
}
</code></pre>

{% endstep %}
{% endstepper %}

{% hint style="danger" %}
Only supported on React and Next.js
{% endhint %}

{% hint style="warning" %}
React query, chakra and dapp-kit are peer dependencies.
{% endhint %}


# Provider Configuration

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

### Basic Setup

Wrap your app with the `VeChainKitProvider`:

```typescript
'use client';

import { VeChainKitProvider } from "@vechain/vechain-kit";

export function Providers({ children }) {
  return (
    <VeChainKitProvider>
      {children}
    </VeChainKitProvider>
  );
}
```

### Next.js Configuration

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

```typescript
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>
  );
}
```

### Complete Configuration Example

Here's a comprehensive example with all available options:

```typescript
'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={{
        // Brand accent — spinner, focus rings, "Waiting for signature…" headline
        accent: '#3b82f6',
        modal: { backgroundColor: 'black' },
      }}
      
      // Login Modal UI Customization
      loginModalUI={{
        logo: '/your-logo.png',
        description: 'Welcome to our DApp',
      }}
      
      // Login Methods Configuration (variation A default)
      loginMethods={[
        { method: "veworld", gridColumn: 4, isPrimary: true },  // recommended CTA, filled + dot
        { method: "google",  gridColumn: 4 },
        { method: "apple",   gridColumn: 4 },
        { method: "more",    gridColumn: 4 },                   // overflow sub-view (wallets / socials / ecosystem)
      ]}
      
      // 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"],
          },
        },
      }}

      // Contract Address Overrides (optional)
      // Override default contract addresses for custom deployments
      contractAddresses={{
        b3trContractAddress: "0x...",
        vot3ContractAddress: "0x...",
      }}
    >
      {children}
    </VeChainKitProvider>
  );
}
```

### Configuration Options

#### Network Configuration

```typescript
network: {
  type: "main" | "test" | "solo" // Select mainnet or testnet or solo
}
```

#### Fee Delegation

Configure transaction fee sponsorship:

```typescript
feeDelegation: {
  delegatorUrl: string,           // Fee delegation service URL
}
```

#### Login Methods

Configure available authentication methods with a flexible grid layout. Each entry pins a `method`, an optional `gridColumn` (1–4) controlling how many of the 4 columns the button spans, and an optional `isPrimary` flag that promotes the button to the recommended CTA (filled inverted surface + "recommended" green dot). If no entry sets `isPrimary`, the kit falls back to highlighting the first visible method. Only one button per grid is primary; `isPrimary` on the `more` footer link is ignored. The filled treatment currently supports `veworld`, `google`, `apple`, and `github`.

```typescript
loginMethods: [
  // --- Wallets (drives @vechain/dapp-kit programmatically; kit owns the UI) ---
  { method: "veworld",        gridColumn: 4, isPrimary: true },  // recommended CTA — filled, dot
  { method: "sync2",          gridColumn: 4 },
  { method: "wallet-connect", gridColumn: 4 },  // triggers WC's own QR modal

  // --- VeChain native ---
  { method: "vechain",   gridColumn: 4 },       // VeChain cross-app social login
  { method: "ecosystem", gridColumn: 4 },       // x2earn ecosystem apps footer

  // --- Social (routed through VeChain's whitelabel cross-app popup unless you pass `privy`) ---
  { method: "google",  gridColumn: 4 },         // works without `privy` (cross-app intent)
  { method: "apple",   gridColumn: 4 },         // works without `privy`
  { method: "twitter", gridColumn: 4 },         // works without `privy`
  { method: "discord", gridColumn: 4 },         // works without `privy`
  { method: "github",  gridColumn: 4 },         // works without `privy`
  { method: "tiktok",  gridColumn: 4 },         // works without `privy`
  { method: "line",    gridColumn: 4 },         // works without `privy`
  { method: "email",   gridColumn: 4 },         // requires your own `privy` (inline, no popup)
  { method: "passkey", gridColumn: 4 },         // requires your own `privy`
  { method: "sms",     gridColumn: 4 },         // requires your own `privy`
  { method: "more",    gridColumn: 4 },         // sub-view with overflow socials/wallets/ecosystem

  // --- Legacy ---
  { method: "dappkit", gridColumn: 4 },         // Opens dapp-kit's native picker. Preserved for backwards compat.
]
```

**Defaults** (when `loginMethods` is omitted):

* With `privy`: `[veworld, google, apple, more]`
* Without `privy`: `[veworld, sync2, wallet-connect]`

**Gating**: granular wallet methods (`veworld`, `sync2`, `wallet-connect`) only render when their source is also in `dappKit.allowedWallets`.

For the full layout, theming and migration guide, see [Login Customization](/quickstart/login-customization).

#### Contract Address Overrides

Override default contract addresses for custom deployments on solo or testnet. Only the provided fields are overridden; the rest use the network defaults.

```typescript
contractAddresses: Partial<AppConfig>  // Any field from AppConfig can be overridden
```

This is useful when you deploy your own contract instances (e.g., B3TR, VOT3) and need the kit to use those addresses instead of the built-in defaults:

```typescript
<VeChainKitProvider
  network={{ type: "solo" }}
  contractAddresses={{
    b3trContractAddress: "0x026771d1be764467f8bdb78bb230df10c924b00d",
    vot3ContractAddress: "0xf7a08af15cb3501feee53ebe11f4428a966fa459",
    // You can override any AppConfig field — see Configs page for the full list
  }}
>
  {children}
</VeChainKitProvider>
```

To access the merged config (network defaults + your overrides) in your components, use the `useAppConfig` hook:

```typescript
import { useAppConfig } from '@vechain/vechain-kit';

function MyComponent() {
  const config = useAppConfig();
  console.log(config.b3trContractAddress); // Your overridden address
}
```

### Privy Integration (Optional)

Most social logins work without your own Privy account — the kit routes Google / Apple / X / Discord / GitHub / TikTok / LINE through VeChain's whitelabel cross-app popup automatically. Pass the `privy` prop only if you need email, passkey, SMS, additional OAuth providers, or want the login flow to render entirely inside your dApp instead of in a popup window.

To enable those flows with your own Privy account:

```typescript
<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>
```

### Ecosystem Apps Configuration

Customize which ecosystem apps appear in the login modal:

```typescript
<VeChainKitProvider
  loginMethods={[
    { method: "ecosystem", gridColumn: 4 }
  ]}
  ecosystemApps={{
    allowedApps: ["app-id-1", "app-id-2"], // Find app IDs in Privy dashboard
  }}
>
  {children}
</VeChainKitProvider>
```

### 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


# Setup Privy (optional)

{% hint style="success" %}
**You do not need a Privy account to ship social login.** By default, VeChain Kit routes Google, Apple, X, Discord, GitHub, TikTok, LINE, and the multi-provider "Continue with VeChain" button through VeChain's whitelabel cross-app popup at [`connect.vechain.org`](https://connect.vechain.org) — branded for VeChain, free, and the user keeps a single identity across every kit-integrated dApp.

Self-host Privy only if you need email / passkey / SMS login, additional OAuth providers, no popup, or branding-inside-your-dApp transaction prompts. See *Pros / Cons* at the bottom of this page.
{% endhint %}

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

```javascript
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>
    );
}
```

Go to[ privy.io](https://privy.io) 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: <https://docs.privy.io/>

This project uses:

* [*@privy-io/react-auth*](https://www.npmjs.com/package/@privy-io/react-auth) for Privy connection type
* [*@privy-io/cross-app-connect*](https://www.npmjs.com/package/@privy-io/cross-app-connect) for ecosystem cross app connection

You can import privy from the kit as

```typescript
import { usePrivy } from "@vechain/vechain-kit";

const { user } = usePrivy();
```

{% hint style="warning" %}
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>
{% endhint %}

{% hint style="info" %}
**Pros of self hosting Privy:**

* No UI confirmations on user transactions (no popup window per signature)
* Allow your users to back up their keys and update security settings directly in your app
* Email / passkey / SMS login (these can't run via the whitelabel popup)
* Additional OAuth providers Privy supports but VeChain's whitelabel doesn't enable (LinkedIn, Spotify, …)
* Your dApp's branding throughout the login modal

**Cons:**

* Price (Privy pricing tiers)
* Responsibility to correctly secure your Privy account, since it contains access to users' wallet settings
* Users have their own wallet scoped to your dApp instead of a single VeChain-wide identity (cross-app linking still possible via ecosystem mode)
  {% endhint %}


# 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.
* **Right:** A **summary view** under `Settings > General > Terms and Policies`, showing which policies they’ve accepted and when.

<figure><img src="/files/7Q8lRRPZdjomQLZW1tXv" alt=""><figcaption></figcaption></figure>

***

{% hint style="info" %}
**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.
  {% endhint %}

```typescript
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>
    );
}

```

**Key Options**

<table><thead><tr><th width="201.77734375">Option</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>allowAnalytics</code></td><td><code>boolean</code></td><td>No</td><td>If <code>true</code>, prompts users with an optional tracking policy.</td></tr><tr><td><code>cookiePolicy</code></td><td><code>array</code></td><td>No</td><td>One or more cookie policy versions.</td></tr><tr><td><code>privacyPolicy</code></td><td><code>array</code></td><td>No</td><td>One or more privacy policy versions.</td></tr><tr><td><code>termsAndConditions</code></td><td><code>array</code></td><td>No</td><td>One or more T&#x26;C versions.</td></tr></tbody></table>


# 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.

<figure><img src="/files/JaCf500g8ge2xqK4lufH" alt=""><figcaption></figcaption></figure>

### 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:

```tsx
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>
    );
}
```

#### Reading Current Values

**Using Convenience Hooks**

The easiest way to access current language and currency values:

```tsx
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>
    );
}
```

**Using useVeChainKitConfig Hook**

You can also access current values through the config hook:

```tsx
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');
}
```

#### Changing Values from Host App

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

```tsx
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>
    );
}
```

#### Listening to Changes

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

```tsx
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>
    );
}
```

{% hint style="warning" %}
In your `i18n.ts` file be sure to check localstorage for setting the language, to avoid issues during refresh.<br>

```typescript
// 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);
    },
};
```

{% endhint %}

### 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
* `setCurrency: (currency: CURRENCY) => void` - Function to change currency
* ... (other config properties)

### 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()` from the host app automatically sync to VeChainKit.
* The sync works bidirectionally - changes in either direction are reflected in both places.


# Login Customization

This guide covers the connect-modal authentication surface in VeChain Kit. From v2.7 onward the kit owns the entire connection UI for VeWorld and Sync2 — there is no hand-off to dapp-kit's native picker.

### What's new in v2.7

* **Custom in-house connection flow** for VeWorld and Sync2. Clicking a wallet button drives `@vechain/dapp-kit` programmatically (`setSource()` + `connect()`) and renders the kit's own "Waiting for signature…" view. WalletConnect's QR modal is preserved because that modal *is* the QR.
* **Granular `loginMethods`**: `veworld`, `sync2`, `wallet-connect`, `apple` are now first-class entries you can pin individually. The legacy `dappkit` value still works.
* **Variation A layout**: one recommended primary CTA (filled inverted + "recommended" dot) — usually VeWorld in the default config, but the dev controls which method gets the emphasis via `isPrimary`; the rest render as outline secondary. "More options ⌄" link footer opens an in-modal sub-view with overflow wallets / socials / ecosystem apps.
* **Themeable accent**: spinner, focus rings and the "Waiting for signature…" headline read from `theme.accent`.

### Overview

VeChain Kit provides four main authentication approaches:

1. **Pre-built `WalletButton`** — fastest, handles state automatically.
2. **Custom button that opens the modal** — `useConnectModal()`.
3. **Custom button that triggers a single wallet directly** — `useConnectWithDappKitSource(source, setContent)`.
4. **Wallet-only via the legacy dapp-kit modal** — `useDAppKitWalletModal()`.

### Method 1: WalletButton

The simplest path. Renders the kit's standard login pill, owns connection state, and switches between login / profile automatically.

```tsx
'use client';
import { WalletButton } from '@vechain/vechain-kit';

export function Page() {
  return <WalletButton />;
}
```

### Method 2: Custom button → kit's connect modal

```tsx
'use client';
import { useConnectModal, useAccountModal, useWallet } from '@vechain/vechain-kit';

export function CustomAuthButton() {
  const { connection } = useWallet();
  const { open: openConnect } = useConnectModal();
  const { open: openAccount } = useAccountModal();

  return connection.isConnected ? (
    <button onClick={openAccount}>View account</button>
  ) : (
    <button onClick={openConnect}>Sign in</button>
  );
}
```

### Method 3: Trigger one wallet, no grid

Skip the grid entirely and open the kit's "Waiting for signature…" view straight away — useful when your app is wallet-specific.

```tsx
'use client';
import { useConnectWithDappKitSource, useModal } from '@vechain/vechain-kit';

export function ConnectWithVeWorld() {
  const { setConnectModalContent, openConnectModal } = useModal();
  const { connect } = useConnectWithDappKitSource('veworld', setConnectModalContent);

  return (
    <button onClick={async () => {
      openConnectModal();           // show the modal as the loading surface
      await connect();              // drives setSource('veworld') + connect()
    }}>
      Connect VeWorld
    </button>
  );
}
```

Allowed sources: `'veworld' | 'sync2' | 'wallet-connect'`. WalletConnect will additionally pop its own QR modal.

### Method 4: OAuth from a custom UI (Privy)

For self-hosted Privy apps, you can drive OAuth flows directly:

```tsx
import { useLoginWithOAuth } from '@vechain/vechain-kit';

const { initOAuth } = useLoginWithOAuth();
await initOAuth({ provider: 'google' }); // 'google' | 'apple' | 'github' | 'discord' | 'twitter' | 'linkedin'
```

### Configuring the grid: `loginMethods`

The order and shape of the connect-modal grid is controlled by `loginMethods` on `<VeChainKitProvider>`. Each entry has a `method`, an optional `gridColumn` (1–4 — buttons span that many of the 4 columns), and an optional `isPrimary` flag that controls which button gets the recommended-CTA treatment.

```tsx
<VeChainKitProvider
  privy={{
    appId: '...',
    clientId: '...',
    loginMethods: ['google', 'apple', 'email', 'twitter', 'discord'],
    appearance: { /* ... */ },
  }}
  dappKit={{
    allowedWallets: ['veworld', 'sync2', 'wallet-connect'],
    walletConnectOptions: { /* ... */ },
  }}
  loginMethods={[
    { method: 'veworld', gridColumn: 4, isPrimary: true },  // recommended CTA — filled, "recommended" dot
    { method: 'google',  gridColumn: 4 },                   // outline secondary
    { method: 'apple',   gridColumn: 4 },                   // outline secondary
    { method: 'more',    gridColumn: 4 },                   // opens an in-modal sub-view
  ]}
>
```

#### Recommended primary CTA — `isPrimary`

One method per grid renders as the recommended CTA: filled inverted surface (dark on light mode, white on dark mode) with a small green dot. The rest render as outline secondary. Two ways the kit picks which:

1. **Explicit** — any entry with `isPrimary: true` (excluding `more`, which is a footer link). If multiple entries set `isPrimary`, the first one wins.
2. **Implicit fallback** — when no entry sets `isPrimary`, the kit highlights the first visible method in the array. So a minimal config like `[{ method: 'google' }, { method: 'apple' }]` still gets Google as the recommended CTA without thinking about emphasis.

Currently supported as primary: `veworld`, `google`, `apple`, `github`. Other methods can sit on the main grid but won't switch to the filled treatment when first / `isPrimary: true` — they keep their outline look. (Adding more is a one-prop change per button; open an issue if you need it.)

```tsx
// Default: Google is primary because it's first.
loginMethods={[
  { method: 'google',  gridColumn: 4 },
  { method: 'apple',   gridColumn: 4 },
  { method: 'more',    gridColumn: 4 },
]}

// Explicit: Apple is primary, Google is outline.
loginMethods={[
  { method: 'google',  gridColumn: 4 },
  { method: 'apple',   gridColumn: 4, isPrimary: true },
  { method: 'more',    gridColumn: 4 },
]}
```

#### Method values

| Method           | Requires                                                                                | Renders                                                                                  |
| ---------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `veworld`        | `dappKit.allowedWallets` includes `'veworld'`                                           | Custom VeWorld flow + the kit's "Waiting for signature…" view                            |
| `sync2`          | `dappKit.allowedWallets` includes `'sync2'`                                             | Custom Sync2 flow + same waiting view                                                    |
| `wallet-connect` | `dappKit.allowedWallets` includes `'wallet-connect'` + `walletConnectOptions.projectId` | Triggers WalletConnect's QR modal programmatically (kit's loading view sits behind)      |
| `google`         | `privy`                                                                                 | Privy Google OAuth (full color "G")                                                      |
| `apple`          | `privy`                                                                                 | Privy Apple OAuth                                                                        |
| `github`         | `privy`                                                                                 | Privy GitHub OAuth                                                                       |
| `email`          | `privy`                                                                                 | Inline email pill + 6-digit code modal                                                   |
| `passkey`        | `privy`                                                                                 | Privy WebAuthn                                                                           |
| `vechain`        | `privy`                                                                                 | VeChain cross-app login (single wallet across Privy ecosystem apps)                      |
| `ecosystem`      | —                                                                                       | Footer button that opens a sub-view listing x2earn ecosystem apps                        |
| `more`           | —                                                                                       | "More options ⌄" link footer → in-modal sub-view with all overflow options               |
| `dappkit`        | `dappKit`                                                                               | **Legacy.** Opens dapp-kit's native picker modal — preserved for backwards compatibility |

#### The `'more'` sub-view

When the user taps **More options ⌄**, the modal cross-fades into a sub-view that surfaces *everything you configured but didn't put on the main grid*:

* **Other wallets** — entries in `dappKit.allowedWallets` not on the main grid (VeWorld / Sync2 / WalletConnect).
* **Other sign-in** — natively-rendered Privy methods in `privy.loginMethods` (Google, Apple, GitHub, email, passkey). Anything else you configured in Privy (Twitter, Discord, Farcaster, TikTok, LinkedIn, …) is reachable via a fallback link that opens Privy's own modal.
* **Ecosystem apps** — the x2earn apps configured via Privy ecosystem.

Items already shown on the main grid are excluded. Sections collapse when they'd be empty. The dev can hide the whole sub-view by omitting `'more'` from `loginMethods`.

#### Default `loginMethods`

If you don't pass `loginMethods`, the kit picks a sensible default:

```ts
// With Privy:
loginMethods = [
  { method: 'veworld', gridColumn: 4 },
  { method: 'google',  gridColumn: 4 },
  { method: 'apple',   gridColumn: 4 },
  { method: 'more',    gridColumn: 4 },
];

// Without Privy:
loginMethods = [
  { method: 'veworld',        gridColumn: 4 },
  { method: 'sync2',          gridColumn: 2 },
  { method: 'wallet-connect', gridColumn: 2 },
];
```

#### Special: only `dappkit`

When `loginMethods` is exactly `[{ method: 'dappkit', ... }]`, `useConnectModal().open()` opens dapp-kit's native picker directly — the kit's modal is never rendered. This preserves the v2.6.x behavior for apps that explicitly pinned `dappkit`.

### Theming the connect modal

The modal honors the theme tokens passed to `<VeChainKitProvider>`:

* **Modal surface / overlay / borders** — `theme.modal.backgroundColor`, `theme.modal.border`, `theme.modal.rounded`, `theme.overlay.backgroundColor`.
* **Text colors** — `theme.textColor` cascades to primary / secondary / tertiary.
* **Primary button (VeWorld CTA)** — `theme.buttons.primaryButton.{bg, color, border, rounded, hoverBg}`.
* **Brand accent** — `theme.accent` controls the spinner top arc, focus rings, the "Waiting for signature…" headline and the email-submit link when the address is valid. Default `#3b82f6` (light) / `#60a5fa` (dark).

```tsx
<VeChainKitProvider
  theme={{
    accent: '#ff6600',
    buttons: {
      primaryButton: { bg: '#0E0D18', color: '#FFFFFF', rounded: '16px' },
    },
  }}
>
```

**Brand-locked surfaces** (intentionally not themed): Google's white tile + colored "G", Apple's glyph, WalletConnect's `#3B99FC`, GitHub's `#24292e`, the recommended-provider green dot on VeWorld. These have to remain recognisable as brand icons.

### VeWorld mobile in-app browser

When your dApp is opened from inside the VeWorld mobile wallet browser, the kit detects `window.vechain.isInAppBrowser` and skips the grid entirely — `setSource('veworld') + connectV2(null)` runs as soon as the connect intent is triggered. The user sees no modal because they're already inside the wallet.

### Migrating from v2.6.x

* **No breaking change** for apps that pinned `{ method: 'dappkit' }` — it still opens dapp-kit's modal exactly as before.
* The **default** `loginMethods` changed. If you *did not* pass `loginMethods`, the modal previously rendered `[vechain, ecosystem, dappkit]` and now renders `[veworld, google, apple, more]` (Privy) or `[veworld, sync2, wallet-connect]` (no Privy). To keep the v2.6 grid, pass it explicitly.
* The granular methods (`veworld`, `sync2`, `wallet-connect`) are gated on `dappKit.allowedWallets`. If a method is in your `loginMethods` but its source isn't in `allowedWallets`, the button is hidden.

### Error handling

The kit handles rejections (`'rejected' | 'cancelled' | 'user denied' | 'closed'`) by returning to the main grid silently. Any other error transitions the modal to the redesigned error view (red disc + **Back** / **Try again**). If you drive `useConnectWithDappKitSource` yourself, the same state machine applies — your `setCurrentContent` setter is what flips between loading / error / main.


# Send Transactions

{% hint style="info" %}
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.
{% endhint %}

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.

```typescript
'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,
                }}
            />
        </>
    );
}

```

You can build clauses with some of our available build functions, with our [SDK](https://docs.vechain.org/developer-resources/sdks-and-providers/sdk) or with [connex](https://docs.vechain.org/developer-resources/sdks-and-providers/connex).

If you want to interact directly with the user's smart account read the [Smart Accounts](/social-login/smart-accounts#multiclause-transactions) section.

{% hint style="warning" %}
**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.

```javascript
// ✅ 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);
};
```

{% endhint %}

***

### 🔧 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:

```ts
useSendTransaction({
  ..., //other config
  suggestedMaxGas: 40000000, // Sets the gas limit directly
});
```

#### `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:

```ts
useSendTransaction({
  ..., //other config
  gasPadding: 0.1, // Adds 10% buffer to estimated gas
});
```

#### Summary

<table data-full-width="true"><thead><tr><th>Option</th><th>Type</th><th width="198.84375">Applies Gas Estimation</th><th width="153.10546875">Applies Padding</th><th width="201.01171875">Overwrites Estimation</th></tr></thead><tbody><tr><td><code>suggestedMaxGas</code></td><td>Integer</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td><code>gasPadding</code></td><td>Float (0–1)</td><td>✅</td><td>✅</td><td>❌</td></tr></tbody></table>

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.


# Sign Messages

## Sign Message

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

```typescript
'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>
            )}
        </>
    );
}
```

## Sign Typed Data (EIP712)

Use the `useSignTypedData()` hook to sign structured data.

```typescript
'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>
            )}
        </>
    );
}
```

## 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.

{% hint style="warning" %}
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**.
{% endhint %}

#### Example usage

{% tabs %}
{% tab title="SignatureWrapperProvider.tsx" %}
Create a provider that will handle the signature verification.

```typescript
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>
    </>
  );
};
```

{% endtab %}

{% tab title="main.tsx" %}
Wrap the app with our new provider

```typescript
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>,
  );
})();

```

{% endtab %}

{% tab title="useSignatureVerification.ts" %}
Handle the signature within a custom hook

```typescript
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 };
};
```

{% endtab %}

{% tab title="Backend Validation" %}
On your backend validate the signature as follows:

```typescript
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",
        }),
    };
}

```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
You should **deprecate** this:

```typescript
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>
}
```

{% endhint %}


# Best Practices

Essential patterns for optimal performance, type safety, and maintainability

### Type Safety

```typescript
// ✅ 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");
```

### Query Optimization

```typescript
// ✅ 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
}
```

### Data Transformation

```typescript
// ✅ 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
```

### Error Handling

```typescript
// ✅ 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),
}
```

### Query Key Management

```typescript
// ✅ 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) });
  },
});
```

### Performance Tips

```typescript
// ✅ 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],
  })),
});
```

### Security

```typescript
// ✅ 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';
  }
};
```

### Single Contract Call Pattern

```typescript
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])),
      }),
    },
  });
};
```

### Multiple Contract Calls Pattern

```typescript
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,
  });
};
```

### Transaction Building Pattern

```typescript
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'] });
    },
  });
};
```

### Multi-Clause Transactions

```typescript
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',
        },
      ];
    },
  });
};
```

### Error Handling

```typescript
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;
      },
    },
  });
};
```


# 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.

{% hint style="info" %}
**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
  {% endhint %}

## 2) Privy Cross App (VeChain whitelabel)

This is the default connection type when your app **does not** pass a `privy` prop to `VeChainKitProvider`. Social logins (Google, Apple, X, Discord, GitHub, TikTok, LINE, and the multi-provider "Continue with VeChain" picker) run through VeChain's whitelabel popup at [`connect.vechain.org`](https://connect.vechain.org). The popup handles the OAuth handshake, decodes the transaction in plain language for the user, and posts the signed result back to your dApp.

Why this is the default in v2.7+:

* **Free social login** — no Privy account, no API keys, no billing. Drop a `{ method: 'google' }` button and it works.
* **One identity across the ecosystem** — the user's VeChain address is the same in every kit-integrated dApp, so balances, NFTs and domains follow them.
* **VeChain-branded** — the popup is whitelabeled (logo, palette, dark/light, 17 languages matching the kit). No Privy chrome leaks through.
* **Smart-contract-aware transaction review** — instead of raw hex, the user sees "Send 10 B3TR to vechain.vet" or "Vote FOR on a VeBetterDAO proposal" with verified-contract chips.
* **Auto-recovery from stale sessions** — if the connection record expires, the kit logs the user out and re-opens the login modal automatically.

Use this option unless you have a specific reason to self-host Privy (see option 1).

## 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.

{% hint style="info" %}
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".
{% endhint %}

{% hint style="success" %}
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:

```typescript
import { useDAppKitWalletModal } from '@vechain/vechain-kit';

export const LoginComponent = () => {
  const { open: openWalletModal } = useDAppKitWalletModal();

  return (
    <Button onClick={openWalletModal}>
        Open only "Connect Wallet"
    </Button>
)}
```

{% endhint %}

{% hint style="warning" %}
When your app is opened inside VeWorld mobile wallet, VeWorld is always enforced as a login choice.
{% endhint %}


# 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, [more here](/hooks/blockchain-hooks)
* The following hooks were also removed in order to improve permeances.

<details>

<summary>Deprecated Hooks</summary>

Utils Hooks

* useRoundAppVotes
* useSustainabilityActions

Galaxy Member Hooks

* 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

</details>

{% hint style="info" %}
`useCallClause` is for reading data from smart contracts with automatic\
caching and refetching

`executeMultipleClausesCall` is to execute multiple contract calls in a single batch
{% endhint %}

{% hint style="info" %}
It might require to `rm -rf node_modules yarn.lock && yarn`
{% endhint %}

### 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
5. Verify functionality as you fix each error
6. Ensure you have adequate test coverage before starting

### Getting Help

* **Documentation**: Refer to individual migration guide sections
* **GitHub Issues**: [Report issues](https://github.com/vechain/vechain-kit/issues)

### Next Steps

1. Start with the [API Changes](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/api-migration-guide) guide to update your core dependencies and imports
2. Review [Contract Patterns](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/api-migration-guide#contract-interactions) to understand new interaction methods
3. Apply [Best Practices](/best-practices) for optimal performance
4. Consult [Troubleshooting](/troubleshooting/general) if you encounter issues


# 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**

* `useConnex` is completely removed and replaced with `useThor`
* This affects all blockchain interactions throughout your application
* [**See API Migration Guide →**](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/api-migration-guide#connex-to-thor)

#### 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 →**](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/api-migration-guide#contract-interactions)

#### 3. **Updated Transaction Building**

* New `useBuildTransaction` hook with improved type safety
* Better error handling and transaction status tracking
* [**See API Migration Guide →**](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/api-migration-guide#transaction-building)

#### 4. **Module Removal**

* Entire VeBetterDAO module removed
* Several utility modules deprecated
* [**See Removed Features →**](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/removed-features-in-v2)

#### 5. **Hook Restructuring**

* Many hooks moved to new locations for better organization
* Import paths have changed significantly
* [**See Hook Relocations →**](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/api-migration-guide)

### 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](https://github.com/vechain/vechain-kit/issues)
* **Documentation**: [VeChain Kit v2 Docs](https://docs.vechain.org/vechain-kit)
* **Community**: [Discord](https://discord.gg/vechain)


# 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

<details>

<summary><strong>View removed core hooks</strong></summary>

**`useConnex`**

* **Removed**: `useConnex` hook
* **Replacement**: Use `useThor`
* **Migration**: [See API Migration Guide](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/api-migration-guide#connex-to-thor)

**`useCall`**

* **Removed**: Generic `useCall` hook
* **Replacement**: Use `useCallClause` with proper typing
* **Migration**: [See API Migration Guide](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/api-migration-guide#reading-contract-data)

**`useEvents`**

* **Status**: Completely refactored with new API
* **Note**: Not removed, but has breaking API changes
* **Documentation**: [Blockchain Hooks](/hooks/blockchain-hooks)

</details>

### VeBetterDAO Module (Completely Removed)

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

<details>

<summary><strong>View all removed VeBetterDAO hooks</strong></summary>

**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`
* `useAppSecurityLevel`
* `useGetCumulativeScoreWithDecay`
* `useGetDelegatee`
* `useGetDelegator`
* `useGetEntitiesLinkedToPassport`
* `useGetPassportForEntity`
* `useGetPendingDelegationsDelegateePOV`
* `useGetPendingDelegationsDelegatorPOV`
* `useGetPendingLinkings`
* `useIsEntity`
* `useIsPassportCheckEnabled`
* `useIsPassport`
* `useParticipationScoreThreshold`
* `useSecurityMultiplier`
* `useThresholdParticipationScore`
* `useThresholdParticipationScoreAtTimepoint`
* `useIsBlacklisted`
* `useIsWhitelisted`
* `useUserRoundScore`

**X2Earn Rewards Pool Hooks**

* `useUserVotesInAllRounds`
* `useUserTopVotedApps`

**XAllocation Pool Hooks**

* `useAllocationAmount`
* `useXAppVotesQf`

**XApps Hooks**

* `useXNode`
* `useAppAdmin`
* `useAppExists`
* `useAppsEligibleInNextRound`
* `useGetX2EarnAppAvailableFunds`
* `useXAppsMetadataBaseUri`
* `useXNodeCheckCooldown`

**XNodes Hooks**

All XNodes-related functionality has been removed.

</details>

### Other Removed Modules

<details>

<summary><strong>View other removed modules</strong></summary>

**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

</details>

### Removed Utility Hooks

<details>

<summary><strong>View removed utility hooks</strong></summary>

**`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

</details>

### Removed Components

<details>

<summary><strong>View removed components</strong></summary>

**`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

</details>

### 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:

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

#### 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)


# 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:

```bash
npm install @vechain/vechain-kit@^2.0.0
npm uninstall @thor-devkit
```

Clean install to avoid conflicts:

```bash
rm -rf node_modules package-lock.json
npm install
```

### Update Imports

Update your import statements throughout your codebase:

```typescript
// Before (1.x)
import { useConnex, useWallet, useTransaction } from '@vechain/vechain-kit';

// After (2.x)
import { useThor, useWallet, useBuildTransaction, useCallClause } from '@vechain/vechain-kit';
```

> **Note**: For the complete list of removed hooks, [see Removed Features](/migrations/upgrade-vechain-kit-from-1.x-to-2.x/removed-features-in-v2)

### Connex to Thor

#### Basic Setup

**v1:**

```typescript
import { useConnex } from '@vechain/vechain-kit';

const Component = () => {
  const connex = useConnex();
  
  // Access thor
  const thor = connex.thor;
  
  // Access vendor
  const vendor = connex.vendor;
};
```

**v2:**

```typescript
import { useThor } from '@vechain/vechain-kit';

const Component = () => {
  const thor = useThor();
  
  // Thor is now directly available
  // Vendor functionality is integrated into transaction methods
};
```

### Contract Interactions

#### Reading Contract Data

**Single Contract Call**

**v1:**

```typescript
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]);
};
```

**v2:**

```typescript
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 };
};
```

**Multiple Contract Calls**

**v1:**

```typescript
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;
};
```

**v2:**

```typescript
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]);
};
```

#### Writing Contract Data (Transactions)

**Simple Transaction**

**v1:**

```typescript
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;
};
```

**v2:**

```typescript
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
  };
};
```

**Multi-Clause Transaction**

**v1:**

```typescript
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;
};
```

**v2:**

```typescript
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 };
};
```

### Transaction Building

#### Advanced Transaction Options

**v1:**

```typescript
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();
};
```

**v2:**

```typescript
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 };
};
```

### Events Handling

The events API has been redesigned in v2. [See more](https://docs.vechainkit.vechain.org/hooks/blockchain-hooks).

**v1:**

```typescript
const { events } = useEvents({
  contractAddress,
  eventName: 'Transfer',
  filters: { from: address }
});
```

**v2:**

```typescript
// New events API - check documentation for updated usage
import { useEvents } from '@vechain/vechain-kit';

// The API has changed - refer to blockchain hooks documentation
```

### Query Keys

#### Generating Query Keys

**v2 introduces specific query key functions:**

```typescript
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 });
```

#### Custom Query Management

```typescript
// Refresh specific contract data
const refreshBalance = () => {
  const key = getCallClauseQueryKeyWithArgs({
    abi: TokenABI,
    address: tokenAddress,
    method: 'balanceOf',
    args: [account]
  });
  
  queryClient.invalidateQueries({ queryKey: key });
};
```

### 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
4. **Leverage Type Safety**: Use TypeScript to catch migration issues
5. **Use Query Keys**: Implement proper cache management with new query key functions


# 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:

```typescript
// 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 [here](/quickstart/send-transactions).

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 [here](/sign-messages).

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.

{% hint style="danger" %}
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.
{% endhint %}


# 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

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.


# Smart Accounts v1 to v3

Upgrading to[ **Smart Account v3**](/social-login/smart-accounts) unlocks **multi-clause support**, **enhanced security**, and other essential features for transactions on VeChain. This feature is available in VeChain Kit from [v1.5.0](https://github.com/vechain/vechain-kit/releases/tag/1.5.0).

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
* **`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](/hooks/intro).

### Example usage

```typescript
"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>
  )
}
```

{% hint style="success" %}
You can customize the color button and size of the imported modal from the kit:

```typescript
const { open: openUpgradeSmartAccountModal } = useUpgradeSmartAccountModal({
    accentColor: '#000000',
    modalSize: 'xl',
});
```

{% endhint %}

### Example demo

#### With UI from the Kit

{% embed url="<https://streamable.com/hyritw>" %}

#### With custom UI

{% embed url="<https://streamable.com/wrpzo4>" %}


# 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 [TanStack Query](https://tanstack.com/query) (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:

* `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:

```typescript
const queryClient = useQueryClient();
// Invalidate all blockchain queries
queryClient.invalidateQueries({ queryKey: ['VECHAIN_KIT'] });
// Invalidate specific query
queryClient.invalidateQueries({ queryKey: ['VECHAIN_KIT', 'CURRENT_BLOCK'] });
```

### 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.


# 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

```typescript
import { useWallet } from "@vechain/vechain-kit"; 

function MyComponent = () => {
    const {
        account,
        connectedWallet,
        smartAccount,
        privyUser,
        connection,
        disconnect
    } = useWallet();
    
    return <></>
}


```

### Types

{% tabs %}
{% tab title="Wallet" %}

```typescript
export type Wallet = {
    address: string;
    domain?: string;
    image: string;
} | null;
```

{% endtab %}

{% tab title="SmartAccount" %}

```typescript
export type SmartAccount = Wallet & {
    isDeployed: boolean;
    isActive: boolean;
    version: string | null;
};
```

{% endtab %}

{% tab title="Connection source" %}

```typescript
export type ConnectionSource = {
    type: 'privy' | 'wallet' | 'privy-cross-app';
    displayName: string;
};
```

{% endtab %}

{% tab title="PrivyUser" %}

```typescript
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;
}
```

{% endtab %}
{% endtabs %}

### 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 [here](#smartaccount-smartaccount).
* `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.


# Login

## Login Hooks

The hooks provide authentication methods for VeChain applications:

### Authentication Hooks

* `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

```typescript
// 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>;
}
```

### Usage example

```typescript
// 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
*/
```


# 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.

<figure><img src="/files/UHLsaRvcfQEfne1lJrRK" alt="" width="322"><figcaption></figcaption></figure>

### Types of Records

Here are some of the most commonly used records:

| Name        | Usage                                                                | Reference                                     | Example                                     |
| ----------- | -------------------------------------------------------------------- | --------------------------------------------- | ------------------------------------------- |
| display     | Preferred capitalization                                             | [ENSIP-5](https://docs.ens.domains/ensip/5)   | Luc.eth                                     |
| avatar      | Avatar or logo (see [Avatars](https://docs.ens.domains/web/avatars)) | [ENSIP-5](https://docs.ens.domains/ensip/5)   | ipfs\://dQw4w9WgXcQ                         |
| description | Description of the name                                              | [ENSIP-5](https://docs.ens.domains/ensip/5)   | DevRel @ ENS Labs                           |
| keywords    | List of comma-separated keywords                                     | [ENSIP-5](https://docs.ens.domains/ensip/5)   | person, ens                                 |
| email       | Email address                                                        | [ENSIP-5](https://docs.ens.domains/ensip/5)   | <luc@ens.domains>                           |
| mail        | Physical mailing address                                             | [ENSIP-5](https://docs.ens.domains/ensip/5)   | V3X HQ                                      |
| notice      | Notice regarding this name                                           | [ENSIP-5](https://docs.ens.domains/ensip/5)   | This is a notice                            |
| location    | Generic location (e.g. "Toronto, Canada")                            | [ENSIP-5](https://docs.ens.domains/ensip/5)   | Breda, NL                                   |
| phone       | Phone number as an E.164 string                                      | [ENSIP-5](https://docs.ens.domains/ensip/5)   | +1 234 567 890                              |
| url         | Website URL                                                          | [ENSIP-5](https://docs.ens.domains/ensip/5)   | [https://ens.domains](https://ens.domains/) |
| header      | Image URL to be used as a header/banner                              | [ENSIP-18](https://docs.ens.domains/ensip/18) | ipfs\://dQw4w9WgXcQ                         |

### 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 <a href="#header-banner-record" id="header-banner-record"></a>

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 <a href="#setting-records" id="setting-records"></a>

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.

{% hint style="info" %}
This is protocol build by ENS domains and supported by veDelegate. Follow [ENS documentation ](https://docs.ens.domains/web/records)for more information.
{% endhint %}

## Keep reading how to integrate with the VeChain Kit

{% content-ref url="/pages/9P33iNi8YgsjndTZZBg9" %}
[vetDomains](/hooks/vetdomains)
{% endcontent-ref %}

{% content-ref url="/pages/9P33iNi8YgsjndTZZBg9" %}
[vetDomains](/hooks/vetdomains)
{% endcontent-ref %}


# 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

```typescript
// 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;
}
```

### Usage example

```typescript
// 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)
*/
```


# Blockchain Hooks

### `useCurrentBlock()`

Fetches the current block from the blockchain with automatic updates.

```typescript
const { data, isLoading, error } = useCurrentBlock();
```

Features:

* Auto-refreshes every 10 seconds
* Caches data for 1 minute
* Returns the latest expanded block information

Example:

```typescript
function BlockInfo() {
  const { data: block } = useCurrentBlock();
  
  return <div>Current Block: {block?.number}</div>;
}
```

### `useTxReceipt()`

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

```typescript
const { data, isLoading, error } = useTxReceipt(txId, blockTimeout);
```

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:

```typescript
function TransactionStatus({ txId }) {
  const { data: receipt, isLoading } = useTxReceipt(txId);
  
  if (isLoading) return <div>Loading...</div>;
  return <div>Transaction Status: {receipt?.reverted ? 'Failed' : 'Success'}</div>;
}
```

## Utility Functions

### **`useEvents()`**

Fetches events from the blockchain based on specified criteria.

```typescript
const events = await getEvents({
   abi,
   contractAddress,
   eventName,
   filterParams,
   mapResponse,
   nodeUrl,
});
```


# Oracle

### `useGetTokenUsdPrice`

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

#### Usage

```typescript
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>;
}
```

### 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                                     |
| --------- | ------------- | ----------------------------------------------- |
| 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.   |

### 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

```typescript
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>
  );
}
```


# NFTs

## NFT Hooks

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

### NFT Data Hooks

* `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

```typescript
// 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

```typescript
// 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;
    }[];
}
*/
```


# Ipfs

## IPFS Hooks

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

### Image Hooks

* `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

```typescript
// 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
*/
```


# 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

```typescript
// 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)
*/
```


# 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.
* `useGetResolverAddress`: Gets the resolver contract address for a domain
* `useUpdateTextRecord`: Updates text records for a domain with transaction handling

### Subdomain Management Hooks

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

### Usage Example

```typescript
/**
 * 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
*/
```


# 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.

```tsx
<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>
```

### 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:

```tsx
overlay: {
    backgroundColor: 'rgba(0, 0, 0, 0.6)', // Overlay background color
    blur: 'blur(10px)', // Overlay blur effect
}
```

### Complete Example

Here's a complete example with glass effects:

```tsx
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>;
```


# Buttons

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

**Secondary Buttons** (applies to all `vechainKitSecondary` buttons):

```tsx
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):

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

**Tertiary Buttons** (applies to all `vechainKitTertiary` buttons):

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

**Login Buttons** (applies to `loginIn` variant):

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

You can customize multiple button types in one config:

```tsx
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)',
    },
}
```


# Fonts

Customize fonts used throughout VeChain Kit components:

```tsx
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:

```tsx
// Only customize font family
fonts: {
    family: 'Inter, sans-serif',
}

// Only customize font sizes
fonts: {
    sizes: {
        medium: '15px',
        large: '18px',
    },
}
```


# Isolated Modals

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

{% hint style="success" %}
Use the { isolatedView: true } prop to show to not allow the user to browse other sections of the kit.
{% endhint %}

### 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
* `useReceiveModal`: Token receiving interface

### Additional Feature Modals

* `useExploreEcosystemModal`: VeChain ecosystem explorer
* `useNotificationsModal`: Notification center
* `useFAQModal`: Frequently asked questions

### Types

```typescript
// 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>;
}
```

### Usage example

```typescript
// 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
*/
```


# 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.

{% embed url="<https://prod-vechainkit-docs-images-bucket.s3.eu-west-1.amazonaws.com/mockup.mp4>" %}

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.

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

```


# Glass Effect

Enable and configure glass morphism effects:

```tsx
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

<figure><img src="/files/aR6SQRCvBhMAO1pOJSXx" alt=""><figcaption></figcaption></figure>

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).


# 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.

```typescript
const { open: openProfileModal } = useProfileModal();

// Open profile modal in isolated mode
openProfileModal({ isolatedView: true });
```

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.
* **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](https://vechainkit.vechain.org/) to see all the components in action.


# 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

```typescript
'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>
        </>
    );
}

```


# 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.

<div align="center"><figure><img src="/files/Hu6vJvz6VCbyGbUCPfmH" alt="" width="375"><figcaption></figcaption></figure></div>

### Usage example

```typescript
'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,
                }}
            />
        </>
    );
}

```


# 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 [Troubleshooting section](/troubleshooting/general).

Contact us on Discord: <https://discord.gg/wGkQnPpRVq>

Open an issue on Github: <https://github.com/vechain/vechain-kit/issues>

{% hint style="warning" %}
**Use yarn or npm to build the packages. pnpm is known to have some issues we are trying to solve.**
{% endhint %}

### 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](/troubleshooting/migration-issues)

Problems when upgrading from DApp Kit or managing dependencies

* Peer Dependencies
* From DApp Kit

#### [🎨 Styling Issues](/troubleshooting/styling-issues)

CSS conflicts and theming problems

* Chakra UI Conflicts
* CSS Framework Conflicts

#### [⚙️ Integration Issues](/troubleshooting/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](https://github.com/vechain/vechain-kit/issues) or ask on [Discord](https://discord.com/invite/vechain).


# General

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

### TypeScript Compilation Errors

#### Type Mismatch Errors

```typescript
// 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);
```

#### BigInt Serialization Errors

```typescript
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
    },
  },
})

```

### React Query Issues

#### Cache Invalidation Not Working

```typescript
// ✅ 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 });
```

### Contract Interaction Issues

**Migration from manual ABI lookup:**

```typescript
// 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 },
});
```

#### Transaction Building Fails

```typescript
// 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`,
  };
};
```

### Performance Issues

#### Too Many Network Requests

```typescript
// ✅ 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
  });
};
```

### Network Issues

#### RPC Endpoint Problems

```typescript
// ✅ 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),
  },
});
```

### Testing Issues

#### Mocking Problems

```typescript
// ✅ Mock VeChain Kit hooks
jest.mock('@vechain/vechain-kit', () => ({
  useWallet: () => ({
    account: { address: '0x123...' },
    isConnected: true,
  }),
  useCallClause: () => ({
    data: [BigInt('1000000000000000000')],
    isLoading: false,
    error: null,
  }),
}));
```


# 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
* **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](#peer-dependencies)

Resolving version conflicts and dependency mismatches when installing VeChain Kit.

#### [From DApp 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](https://github.com/vechain/vechain-kit/issues) or ask on [Discord](https://discord.com/invite/vechain).


# 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:

```bash
# Remove existing installations
rm -rf node_modules
rm package-lock.json # or yarn.lock

# Reinstall packages
npm install # or yarn install
```

2. **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`

3. **Update or Downgrade Packages**

You may need to adjust package versions to maintain compatibility:

```bash
# 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/dapp-kit-react@2.0.1
```


# From DApp Kit

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

{% hint style="info" %}
VeChain Kit includes DApp Kit functionality, but still requires @vechain/dapp-kit-react as a peer dependency for compatibility.
{% endhint %}

### Problem

Multiple versions of DApp Kit packages can cause:

* 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:

```bash
npm uninstall @vechain/dapp-kit @vechain/dapp-kit-react @vechain/dapp-kit-ui
```

2. Completely Remove DApp Kit Packages

VeChain Kit provides similar functionality with updated component names:

```bash
// X Old DApp Kit
import { ConnectWallet } from '@vechain/dapp-kit-react';

// ✅ New VeChain Kit
import { WalletButton } from '@vechain/vechain-kit';
```

3. Clean Installation

After removing old packages:

```bash
rm -rf node_modules package-lock.json
npm install
```

**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


# 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

#### [Chakra Conflicts](#chakra-conflicts)

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

#### [Chakra v3 host: useToken returns a snapshot](#chakra-v3-host-usetoken-returns-a-snapshot-not-a-css-variable)

Why piping `useToken('colors', [...])` from a Chakra v3 host into the Kit's `theme` prop freezes colors at first render, and how to keep them reactive with `sys.token.var(...)`.

#### [CSS Framework Conflicts](#css-framework-conflicts)

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

***

**Can't find your issue?** Search our [GitHub Issues](https://github.com/vechain/vechain-kit/issues) or ask on [Discord](https://discord.com/invite/vechain).


# 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
* 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:

```bash
yarn add @chakra-ui/react@2.8.2
```

#### Setup ChakraProvider

Wrap your app with `ChakraProvider` and include `ColorModeScript`:

<figure><img src="/files/BS5OGNXjHoWW1rhWo32O" alt=""><figcaption><p>One common issue, solvable by installing Chakra and defining &#x3C;ColorModeScript /></p></figcaption></figure>

```bash
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>
);
```

#### Key Requirements

The essential setup:

```bash
<ChakraProvider theme={stargateStakingTheme}>
    <ColorModeScript initialColorMode="dark" />
    <AppWrapper />
</ChakraProvider>
```

#### **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.


# Chakra v3 host: useToken returns a snapshot

If your app is on Chakra UI v3 and you pipe its theme tokens into the Kit's `theme` prop, you may see the Kit's modal stuck on light or dark colors no matter how you toggle the host theme. The Kit isn't broken — Chakra v3's `useToken` is returning a literal color snapshot, not a CSS variable reference.

### Symptom

* `next-themes` (or any class-based theming) toggles `html.class="dark"` correctly
* The host's own surfaces re-render (CSS variables resolve to the new mode)
* But the Kit's modal, card, sticky header, and other surfaces are stuck on whatever color was active when the page first hydrated
* Only Kit components that read `useVeChainKitConfig().darkMode` directly (e.g. the VeWorld button) update after a toggle

### Root cause

Chakra UI v3's `useToken` returns the **resolved value** at call time, not the CSS variable reference:

```js
// node_modules/@chakra-ui/react/dist/esm/styled-system/system.js
const tokenFn = (path, fallback) => tokenMap.get(path)?.value || fallback;
tokenFn.var = (path, fallback) => tokenMap.get(path)?.variable || fallback;
```

A wrapper like:

```tsx
// ❌ Snapshots the resolved color at render time
const [bgPrimary] = useToken('colors', ['bg.primary'])
// bgPrimary === '#1B1D1F'    when Chakra evaluated bg.primary in dark scope
// bgPrimary === 'white'      when it evaluated in light scope
```

is darkMode-frozen by the time the value reaches the Kit. The Kit receives a literal color and has no way to know it was meant to be reactive.

### Fix

Pass the **CSS variable reference** to the Kit so the underlying color stays governed by `html.class`. Two equivalent options:

#### Option A — use Chakra v3's `sys.token.var(...)` resolver

```tsx
import { useChakraContext } from '@chakra-ui/react'

const sys = useChakraContext()
const tokVar = (path: string) => sys.token.var(`colors.${path}`) as string

const bgPrimary       = tokVar('bg.primary')
const primaryDefault  = tokVar('actions.primary.default')
const primaryText     = tokVar('actions.primary.text')
const primaryHover    = tokVar('actions.primary.hover')
const secondaryDefault = tokVar('card.subtle')
const secondaryHover  = tokVar('card.hover')
const borderSecondary = tokVar('border.secondary')

// bgPrimary === 'var(--vbd-colors-bg-primary)'
```

#### Option B — hardcode the CSS variable

If your `cssVarsPrefix` is fixed:

```tsx
const bgPrimary = 'var(--vbd-colors-bg-primary)'
const primaryDefault = 'var(--vbd-colors-actions-primary-default)'
// …etc
```

Either way, the Kit's CSS variables now point at your host's variables, and the underlying color flips at paint time whenever `html.class` flips. No re-render needed.

### How to verify

Open the connect or account modal in light mode, then in DevTools inspect:

```
--chakra-colors-vechain-kit-modal      ← should equal var(--your-prefix-...) or a CSS variable
--your-prefix-colors-bg-primary        ← should switch between values on theme toggle
```

If `--chakra-colors-vechain-kit-modal` resolves to a hex literal like `#1B1D1F` when the host is in light mode, your wrapper is still using `useToken(...).value`. Switch to `sys.token.var(...)`.

### Reference reproduction

The `examples/next-chakra-v3` workspace in [vechain/vechain-kit](https://github.com/vechain/vechain-kit) mirrors this exact host stack (Next.js + React 18 + Chakra v3 + next-themes) and demonstrates the correct wrapper pattern.


# 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

* 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:

```css
/* 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 */
}
```

**Framework-Specific Examples**

**Tailwind CSS**

```bash
@layer vechain-kit, host-app;

@layer host-app {
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
}
```

**Tailwind v4**

```
@layer vechain-kit, host-app;

/* Import Tailwind v4 */
@import "tailwindcss";

/* Your custom styles */
@layer host-app {
    /* Custom overrides */
}
```

Bootstrap

```
@layer vechain-kit, host-app;

@layer host-app {
    @import 'bootstrap/dist/css/bootstrap.css';
}
```

Custom CSS

<pre><code><strong>@layer vechain-kit, host-app;
</strong>
@layer host-app {
    body {
        /* Your body styles */
    }

    .your-components {
        /* Component styles */
    }
}
</code></pre>

Quick Fixes for Common Issues

<pre><code><strong>@layer host-app {
</strong>    /* Fix image borders */
    img {
        border-style: solid !important;
    }

    /* Ensure body styles persist */
    html body {
        background: var(--your-bg);
        font-family: var(--your-font);
    }
}
</code></pre>

**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+


# 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

[Fee Delegation](/troubleshooting/integration-issues/fee-delegation)

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

#### [Privy Popup Blocking](#privy-popup-blocking)

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

***

**Can't find your issue?** Search our [GitHub Issues](https://github.com/vechain/vechain-kit/issues) or ask on [Discord](https://discord.com/invite/vechain).


# 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
* 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

{% hint style="danger" %}
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.
{% endhint %}

2. **Configure VeChain Kit Fee Delegation**

Simply provide the delegation URL in the VeChainKitProvider:

<pre class="language-javascript"><code class="lang-javascript">&#x3C;VeChainKitProvider
<strong>  feeDelegation={{
</strong>    delegatorUrl: process.env.NEXT_PUBLIC_DELEGATOR_URL,
    delegateAllTransactions: false
  }}
  // ... other config
>
  &#x3C;App />
&#x3C;/VeChainKitProvider>
</code></pre>

3. **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.


# 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:

```javascript
// ✅ 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);
};
```

#### Best Practices

1. **Load Data Early**

<pre class="language-javascript"><code class="lang-javascript">// 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
<strong>    });
</strong>};
</code></pre>

2. **Show Loading States**

```javascript
const MyComponent = () => {
    const { data, isLoading } = useQuery(['requiredData'], fetchData);

    return (
      <button 
        onClick={() => sendTransaction(data)}
        disabled={isLoading}
      >
        {isLoading ? 'Preparing...' : 'Send Transaction'}
      </button>
    );
  };

```

3. **Avoid Async in Click Handlers**

```javascript
// ❌ Avoid
onClick={async () => {
  const result = await someAsyncOperation();
  sendTransaction(result);
}}

// ✅ Better
onClick={() => {
  sendTransaction(preLoadedData);
}}
```

#### **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


# 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.
* **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.

{% hint style="success" %}
By using this default option, you, as an app owner, won't spend any money for the user operations.
{% endhint %}

### **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.


# 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
* Social login users (email, Google, etc.)
* Improving overall user onboarding

### Configuration

Add fee delegation to your VeChainKitProvider:

```typescript
<VeChainKitProvider
  feeDelegation={{
    delegatorUrl: "YOUR_FEE_DELEGATION_URL",
    delegateAllTransactions: true, // or false for social login only
  }}
>
  {children}
</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)**

```typescript
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': '*'
      }
    });
  }
}
```

**Environment Variables**

```bash
# .env
SIGNER_MNEMONIC="your twelve word mnemonic phrase here"
```

**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](https://learn.vechain.energy/vechain.energy/FeeDelegation/Setup/).

1. **Visit VeChain.Energy**

   * Go to [vechain.energy](https://vechain.energy)
   * Create an account and project

   <figure><img src="/files/cnG8riED787CtQEiih5L" alt=""><figcaption></figcaption></figure>
2. **Configure Fee Delegation**
   * Navigate to: `Your Project → Fee Delegation → Configurations`
   * Add the VeChain Kit smart contract address:
     * **Mainnet**: `0xD7B96cAC488fEE053daAf8dF74f306bBc237D3f5`
     * **Testnet**: `0x7C5114ef27a721Df187b32e4eD983BaB813B81Cb`
3. **Get Your Delegation URL**
   * Your delegation URL will be: `https://sponsor-testnet.vechain.energy/by/YOUR_PROJECT_ID`
4. **Configure Your Provider**

   ```typescript
   <VeChainKitProvider
     feeDelegation={{
       delegatorUrl: "https://sponsor-testnet.vechain.energy/by/YOUR_PROJECT_ID",
       delegateAllTransactions: false,
     }}
   >
     {children}
   </VeChainKitProvider>
   ```

### Smart Contract for Custom Rules

For advanced delegation rules, deploy a smart contract:

```solidity
// 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;
        }
    }
}
```

### 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

```typescript
// 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();
}
```

### Testing Fee Delegation

1. **Test with Social Login**

   ```typescript
   // 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);
   }
   ```
2. **Verify Delegation Headers.** Check that your delegation service returns proper headers:

   ```json
   {
     "signature": "0x...",
     "address": "0x..."
   }
   ```

### 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


# 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[​](https://docs.privy.io/guide/embedded-wallets#using-wallets-across-apps) <a href="#using-wallets-across-apps" id="using-wallets-across-apps"></a>

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.

<figure><img src="/files/boWXkqze6drZyP4RFzeg" alt=""><figcaption><p>Sample cross app wallet flow</p></figcaption></figure>

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.


# Smart Accounts

Our [smart accounts](https://github.com/vechain/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](https://vechainstats.com/account/0xc06ad8573022e2be416ca89da47e8c592971679a/)

#### Testnet

[0x713b908Bcf77f3E00EFEf328E50b657a1A23AeaF](https://explore-testnet.vechain.org/accounts/0x713b908bcf77f3e00efef328e50b657a1a23aeaf)

{% hint style="info" %}
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.
{% endhint %}

{% hint style="warning" %}
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.
{% endhint %}

### 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:
  * 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

### 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
   * 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.
3. **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
4. **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.

### 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
* **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

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

{% hint style="warning" %}
Testnet Factory address changed from v1 to v3 from `0x7EABA81B4F3741Ac381af7e025f3B6e0428F05Fb` to `0x713b908Bcf77f3E00EFEf328E50b657a1A23AeaF` which will cause all your testnet smart account addresses to change.
{% endhint %}

### 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).

<figure><img src="/files/x258YhhGgpbNPVK3sCEt" alt=""><figcaption><p>Example of the upgrade modal</p></figcaption></figure>

### Hooks

Developers can efficiently manage smart accounts using a [variety of hooks](/hooks/intro) 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.
  * 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.

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` :

<pre class="language-javascript"><code class="lang-javascript">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' &#x26;&#x26; '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 = [];
<strong>    clauses.push(
</strong>        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
    // ...
};
</code></pre>

{% hint style="danger" %}
Currently VeChain Kit does not support the handling of a smart account by a VeWorld wallet.
{% endhint %}


# 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.

{% hint style="warning" %}
Currently, it is mandatory to sponsor transaction for users that use social login. You can deploy your own custom solution or use [vechain.energy](https://vechain.energy/).
{% endhint %}

### FEE\_DELEGATION\_URL

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](https://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.<br>

```typescript
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': '*'
        }
    })
}
```

## Option 2: use [vechain.energy](https://vechain.energy/)

<figure><img src="/files/lkzY47q4PKJhZGYLHSRa" alt=""><figcaption></figcaption></figure>

{% embed url="<https://learn.vechain.energy/vechain.energy/FeeDelegation/Setup/>" %}

### [GO TO TUTORIAL](https://learn.vechain.energy/vechain.energy/FeeDelegation/Setup/)

{% hint style="info" %}
Add this address 0xD7B96cAC488fEE053daAf8dF74f306bBc237D3f5 (MAINNET) or 0x7C5114ef27a721Df187b32e4eD983BaB813B81Cb (TESTNET) in {YourProjectName/FeeDelegation/Configurations/Smart Contract to sponsor all transactions.
{% endhint %}

{% hint style="info" %}

```solidity
/// 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;
    }
}
```

{% endhint %}

{% hint style="info" %}
Enable email notifications to let you know of low VTHO balance.
{% endhint %}

## 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)](https://docs.vechain.org/thor/learn/fee-delegation)

### 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)](https://docs.vechain.org/tutorials/how-to-integrate-VIP-191-1.html).

### 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/create-vechain-dapp

This is your one-stop solution for kickstarting development on the Vechain blockchain. Whether you're building a complex X2Earn application, a simple decentralized app, or just working on smart contracts, we've got you covered with our carefully crafted templates. Each template comes with pre-configured tools, best practices, and comprehensive documentation to help you start building right away.

### Usage 📦

```
npm create vechain-dapp
```

or

```
yarn create vechain-dapp
```

or

```
npx create-vechain-dapp@latest
```

### NPM Package

<https://www.npmjs.com/package/create-vechain-dapp>


# React dApp Template 🛠️

A streamlined monorepo setup using Turbo with:

* React frontend ⚛️
* Hardhat for smart contract development 🔨
* Basic Dapp infrastructure

Ideal for developers starting new projects on VeChain from scratch.

### Github


# Smart Contract Template 📜

A focused template for smart contract development:

* Hardhat development environment 🔨
* Contract deployment tools 🚀
* Testing infrastructure 🧪

Best suited for developers focusing solely on smart contract development without frontend requirements.

Each template includes detailed documentation and setup instructions in its respective directory.


# X2Earn Template 💰

A comprehensive monorepo setup using Turbo with:

* React frontend ⚛️
* Express.js backend 🚀
* Hardhat for smart contract development 🔨
* Advanced features including ChatGPT image recognition 🖼️
* VeBetterDAO contract interactions 🤝
* Complete X2Earn application infrastructure

Perfect for building sophisticated X2Earn applications on VeChain.

### Github

{% embed url="<https://github.com/vechain/x-app-template>" %}

## How to build a Sustainability DApp on VeChain by using the template

{% embed url="<https://www.youtube.com/watch?ab_channel=PieroBassa&v=fmm2IDMuYjE>" %}

<br>


# Intro

{% hint style="danger" %}
Version 1 is DEPRECATED and won't be mantained. Please migrate to [Version 2](https://docs.vechainkit.vechain.org/intro/new-in-v2).
{% endhint %}

<figure><img src="/files/t9wlePThlbdHYhj2D2vP" alt=""><figcaption></figcaption></figure>

**VeChain Kit** is a comprehensive library designed to make building VeChain applications fast and straightforward. It offers:

* **Seamless Wallet Integration:** Support for VeWorld, Sync2, WalletConnect, VeChain Embedded Wallet, and social logins (powered by Privy).
* **Unified Ecosystem Accounts:** Leverage Privy’s Ecosystem feature to give users a single wallet across multiple dApps, providing a consistent identity within the VeChain network.
* **Developer-Friendly Hooks:** Easy-to-use React Hooks that let you read and write data on the VeChainThor blockchain.
* **Pre-Built UI Components:** Ready-to-use components (e.g., `TransactionModal`) to simplify wallet operations and enhance your users’ experience.
* **Multi-Language Support:** Built-in i18n for a global audience.
* **Token Operations:** Send tokens, check balances, manage VET domains, and more—all in one place.

**Easier Integration**\
We provide a standardized “kit” that quickly integrates social logins and VeChain Smart Accounts—without the hassle of manual contract deployment or configuration.

## Resources

VeChain Kit Demo: <https://vechain-kit.vechain.org/>

Smart Account Factory: <https://vechain.github.io/smart-accounts-factory/>

NPM: <https://www.npmjs.com/package/@vechain/vechain-kit>

Check our [Troubleshooting section](/vechain-kit-v1.x/vechain-kit/troubleshooting).

Contact us on Discord: <https://discord.com/invite/vechain>

Open an issue on Github: <https://github.com/vechain/vechain-kit/issues>

## Available imports

* @vechain/vechain-kit&#x20;
* @vechain/vechain-kit/utils
* @vechain/vechain-kit/contracts
* @vechain/vechain-kit/assets


# Quickstart

{% hint style="danger" %}
Version 1 is DEPRECATED and won't be mantained. Please migrate to [Version 2](https://docs.vechainkit.vechain.org/intro/new-in-v2).
{% endhint %}

## 1) Install package

{% code overflow="wrap" %}

````sh
# Install dependencies

```bash
yarn add \
  @emotion/styled@^11.14.0 \
  @emotion/react@^11.14.0 \
  framer-motion@^11.14.0 \
  @tanstack/react-query@^5.64.2 \
  @chakra-ui/react@^2.8.2 \
  @vechain/dapp-kit-react@2.0.4 \
  @vechain/sdk-core@1.2.0 \
  @vechain/sdk-network@1.2.0

```

# Install vechain kit

```bash
yarn @vechain/vechain-kit@1.10.4
```
````

{% endcode %}

{% hint style="danger" %}
Only supported on React and Next.js
{% endhint %}

{% hint style="warning" %}
React query, chakra and dapp-kit are peer dependencies.
{% endhint %}

## 2) Define Provider

```typescript
'use client';

import {VeChainKitProvider} from "@vechain/vechain-kit";

export function VeChainKitProviderWrapper({children}: any) {
  return (
    <VeChainKitProvider
      feeDelegation={{
        delegatorUrl: "https://sponsor-testnet.vechain.energy/by/441",
        // set to false if you want to delegate ONLY social login transactions
        // social login transactions sponsorship is currently mandatory
        delegateAllTransactions: false,
      }}
      loginMethods={[
        {method: "vechain", gridColumn: 4},
        {method: "dappkit", gridColumn: 4},
      ]}
      dappKit={{
        allowedWallets: ["veworld", "wallet-connect", "sync2"],
        walletConnectOptions: {
          projectId:
            // Get this on https://cloud.reown.com/sign-in
            process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID!,
          metadata: {
            name: "React Dapp Template",
            description: "This is the description of your app visible in VeWorld upon connection request.",
            url: typeof window !== "undefined" ? window.location.origin : "",
            icons: ["https://path-to-logo.png"],
          },
        },
      }}
      darkMode={false}
      language="en"
      network={{
        type: "test",
      }}
    >
      {children}
    </VeChainKitProvider>
  );
}

```

On Next.js you will need to dynamically load the import

```typescript
import dynamic from 'next/dynamic';

const VeChainKitProvider = dynamic(
    async () =>
        (await import('@vechain/vechain-kit')).VeChainKitProvider,
    {
        ssr: false,
    },
);
```

### Login Methods

The modal implements a dynamic grid layout system that can be customized through the `loginMethods` configuration.

The modal can be configured through the `VeChainKitProvider` props.

```typescript
<VeChainKitProvider
    loginModalUI={{
        logo: '/your-logo.png',
        description: 'Custom login description',
    }}
    loginMethods={[
        { method: 'vechain', gridColumn: 4 },
        { method: 'dappkit', gridColumn: 4 }, // VeChain wallets, always available
        { method: 'ecosystem', gridColumn: 4 }, // Mugshot, Cleanify, Greencart, ...
        { method: 'email', gridColumn: 2 }, // only available with your own Privy
        { method: 'passkey', gridColumn: 2 },  // only available with your own Privy
        { method: 'google', gridColumn: 4 }, // only available with your own Privy
        { method: 'more', gridColumn: 2 }, // will open your own Privy login, only available with your own Privy
        
    ]}
    allowCustomTokens={false} // allow the user to manage custom tokens
>
    {children}
</VeChainKitProvider>
```

{% hint style="warning" %}
Login methods selection:

* vechain, dappkit, and ecosystem are always available options
* The Privy-dependent methods (email, google, passkey, more) are only available when the privy prop is defined
* TypeScript will show an error if someone tries to use a Privy-dependent method when privy is not configured

```typescript
// This will show a type error
const invalidConfig: VechainKitProviderProps = {
    // no privy prop specified
    loginMethods: [
        { method: 'email' }  // ❌ Type error: 'email' is not assignable to type 'never'
    ],
    // ... other required props
};

// This is valid
const validConfig1: VechainKitProviderProps = {
    // no privy prop
    loginMethods: [
        { method: 'vechain' },  // ✅ OK
        { method: 'dappkit' },  // ✅ OK
        { method: 'ecosystem' } // ✅ OK
    ],
    // ... other required props
};

// This is also valid
const validConfig2: VechainKitProviderProps = {
    privy: {
        appId: 'xxx',
        clientId: 'yyy',
        // ... other privy props
    },
    loginMethods: [
        { method: 'email' },     // ✅ OK because privy is configured
        { method: 'google' },    // ✅ OK because privy is configured
        { method: 'vechain' },   // ✅ OK (always allowed)
        { method: 'ecosystem' }  // ✅ OK (always allowed)
    ],
    // ... other required props
};
```

{% endhint %}

{% hint style="info" %}
By default we have a list of default apps that will be shown as ecosystem login options. If you want to customize this list you can pass the `allowedApps` array prop. You can find the app ids in the [Ecosystem](https://dashboard.privy.io/) tab in the Privy dashboard.
{% endhint %}

## 3) Setup Fee Delegation (mandatory if allowing social login)

Fee delegation is **mandatory** if you want to use this kit with social login. Learn how to setup fee delegation in the following guide:

{% content-ref url="/pages/GQxTTArVf9E5itgO7t1i" %}
[Fee Delegation](/vechain-kit-v1.x/social-login/fee-delegation)
{% endcontent-ref %}

## 4) Setup Privy (optional)

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

```javascript
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>
    );
}
```

Go to[ privy.io](https://privy.io) 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: <https://docs.privy.io/>

This project uses:

* [*@privy-io/react-auth*](https://www.npmjs.com/package/@privy-io/react-auth) for Privy connection type
* [*@privy-io/cross-app-connect*](https://www.npmjs.com/package/@privy-io/cross-app-connect) for ecosystem cross app connection

You can import privy from the kit as&#x20;

```typescript
import { usePrivy } from "@vechain/vechain-kit";

const { user } = usePrivy();
```

{% hint style="warning" %}
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>
{% endhint %}

{% hint style="info" %}
**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
  {% endhint %}

## 5) 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.
* **Right:** A **summary view** under `Settings > General > Terms and Policies`, showing which policies they’ve accepted and when.

<div align="center" data-full-width="true"><figure><img src="/files/jO1QUm0mtYfenjmjwnuY" alt=""><figcaption><p>Legal Docs Modal</p></figcaption></figure> <figure><img src="/files/4xUDRRRs6S9UQryaim1e" alt=""><figcaption><p>Legal Docs Summary View</p></figcaption></figure></div>

***

{% hint style="info" %}
**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.
  {% endhint %}

```typescript
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>
    );
}

```

**Key Options**

<table><thead><tr><th width="201.77734375">Option</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>allowAnalytics</code></td><td><code>boolean</code></td><td>No</td><td>If <code>true</code>, prompts users with an optional tracking policy.</td></tr><tr><td><code>cookiePolicy</code></td><td><code>array</code></td><td>No</td><td>One or more cookie policy versions.</td></tr><tr><td><code>privacyPolicy</code></td><td><code>array</code></td><td>No</td><td>One or more privacy policy versions.</td></tr><tr><td><code>termsAndConditions</code></td><td><code>array</code></td><td>No</td><td>One or more T&#x26;C versions.</td></tr></tbody></table>

## 6) Show the login button

Once you set up the kit provider, you are good to go, and you can allow your users to login, customizing the login experience based on your needs. You can choose between many options, leaving you complete freedom on your design.

{% tabs %}
{% tab title="1) Use WalletButton component" %}
You can use this component by importing it from the kit, it will handle for you the connection state and show a login button if the user is disconnected or the profile button when the user is connected.

```typescript
'use client';

import { WalletButton } from '@vechain/vechain-kit';

export function Page() {
    return (
        <WalletButton />
    );
}
```

Read more [here](#wallet-button) on how to customize this button.
{% endtab %}

{% tab title="2) Create a custom button" %}
Alternatively, you can create your own custom button and invoke the connect modal or account modal based on your needs.

```typescript
'use client';

import { useConnectModal, useAccountModal, useWallet } from '@vechain/vechain-kit';

export function Page() {
    const { connection } = useWallet();
    
    const { 
        open: openConnectModal, 
        close: closeConnectModal, 
        isOpen: isConnectModalOpen 
    } = useConnectModal();
    
     const { 
        open: openAccountModal, 
        close: openAccountModal, 
        isOpen: isAccountModalOpen 
    } = useAccountModal();
    
    if (!connection.isConnected) {
        return (
            <button onClick={openConnectModal}> Connect </button>
        );
    }
    
    return (
        <button onClick={openAccountModal}> View Account </button>
    );
}
```

{% endtab %}

{% tab title="3) Call targeted login methods" %}
Only available for apps with self hosted Privy .

This is an example of doing login with Google custom button, for more in depth details read [here](/vechain-kit-v1.x/vechain-kit/hooks/login).

```typescript
// Example usage of Login hooks
import { 
    useLoginWithOAuth,
} from '@vechain/vechain-kit';

const ExampleComponent = () => {
    // OAuth authentication
    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>
            {/* 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>
        </div>
    );
};

export default ExampleComponent;
```

{% endtab %}

{% tab title="4) Allow only DAppKit" %}
You can allow users connect to your app only with wallet by using the dapp-kit connect modal, as follows:

```typescript
import { useDAppKitWalletModal } from '@vechain/vechain-kit';

export const LoginComponent = () => {
  const { open: openWalletModal } = useDAppKitWalletModal();

  return (
    <Button onClick={openWalletModal}>
        Open only "Connect Wallet"
    </Button>
)}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
When your app is opened inside VeWorld mobile wallet, VeWorld is always enforced as a login choice.
{% endhint %}

## Support for devs

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 [Troubleshooting section](/vechain-kit-v1.x/vechain-kit/troubleshooting).

Contact us on Discord: <https://discord.gg/wGkQnPpRVq>

Open an issue on Github: <https://github.com/vechain/vechain-kit/issues>


# Migrations

{% hint style="danger" %}
Version 1 is DEPRECATED and won't be mantained. Please migrate to [Version 2](https://docs.vechainkit.vechain.org/intro/new-in-v2).
{% endhint %}

### Migrating Your App: From DAppKit or a Traditional Web2 Application

Follow these steps to seamlessly transition your application from DAppKit or a classic web2 framework.

{% content-ref url="/pages/an1lwXWRBYgB2S5U1SJz" %}
[Migrate from DAppKit](/vechain-kit-v1.x/vechain-kit/migrations/migrate-from-dappkit)
{% endcontent-ref %}

{% content-ref url="/pages/SsR0du1kd10VowNM2Ked" %}
[Migrate Social Login Users](/vechain-kit-v1.x/vechain-kit/migrations/migrate-social-login-users)
{% endcontent-ref %}

{% content-ref url="/pages/qsPqsIHrNQCqsYQOMjUB" %}
[Smart Accounts v1 to v3](/vechain-kit-v1.x/vechain-kit/migrations/smart-accounts-v1-to-v3)
{% endcontent-ref %}


# 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:

```typescript
// 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 [here](/vechain-kit-v1.x/vechain-kit/send-transactions).

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 [here](/vechain-kit-v1.x/vechain-kit/sign-messages).

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.

{% hint style="danger" %}
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.
{% endhint %}


# 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

<figure><img src="/files/K0krZHFYW4qLjEHxPwL3" alt=""><figcaption><p>Privy/YourApp/Users/All Users/Add</p></figcaption></figure>

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.


# Smart Accounts v1 to v3

Upgrading to[ **Smart Account v3**](/vechain-kit-v1.x/social-login/smart-accounts) unlocks **multi-clause support**, **enhanced security**, and other essential features for transactions on VeChain. This feature is available in VeChain Kit from [v1.5.0](https://github.com/vechain/vechain-kit/releases/tag/1.5.0).

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
* **`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](/vechain-kit-v1.x/vechain-kit/hooks/smart-account).

### Example usage

```typescript
"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>
  )
}
```

{% hint style="success" %}
You can customize the color button and size of the imported modal from the kit:

```typescript
const { open: openUpgradeSmartAccountModal } = useUpgradeSmartAccountModal({
    accentColor: '#000000',
    modalSize: 'xl',
});
```

{% endhint %}

### Example demo

#### With UI from the Kit

{% embed url="<https://streamable.com/hyritw>" %}

#### With custom UI

{% embed url="<https://streamable.com/wrpzo4>" %}


# Migrate embedded wallets


# Troubleshooting

Common issues and solutions when using VeChain Kit

{% hint style="warning" %}

### Use yarn or npm to build the packages. pnpm is known to have some issues we are trying to solve.

{% endhint %}

### 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](/vechain-kit-v1.x/vechain-kit/troubleshooting/migration-issues)

Problems when upgrading from DApp Kit or managing dependencies

* Peer Dependencies
* From DApp Kit

#### [🎨 Styling Issues](/vechain-kit-v1.x/vechain-kit/troubleshooting/styling-issues)

CSS conflicts and theming problems

* Chakra UI Conflicts
* CSS Framework Conflicts

#### [⚙️ Integration Issues](/vechain-kit-v1.x/vechain-kit/troubleshooting/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](https://github.com/vechain/vechain-kit/issues) or ask on [Discord](https://discord.com/invite/vechain).


# 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
* **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](#peer-dependencies)

Resolving version conflicts and dependency mismatches when installing VeChain Kit.

#### [From DApp 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](https://github.com/vechain/vechain-kit/issues) or ask on [Discord](https://discord.com/invite/vechain).


# 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:

```bash
# Remove existing installations
rm -rf node_modules
rm package-lock.json # or yarn.lock

# Reinstall packages
npm install # or yarn install
```

2. #### 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`

3. **Update or Downgrade Packages**

You may need to adjust package versions to maintain compatibility:

```bash
# 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/dapp-kit-react@2.0.1
```


# From DApp Kit

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

{% hint style="info" %}
VeChain Kit includes DApp Kit functionality, but still requires @vechain/dapp-kit-react as a peer dependency for compatibility.
{% endhint %}

### Problem

Multiple versions of DApp Kit packages can cause:

* 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:

```bash
npm uninstall @vechain/dapp-kit @vechain/dapp-kit-react @vechain/dapp-kit-ui
```

2. Completely Remove DApp Kit Packages

VeChain Kit provides similar functionality with updated component names:

```bash
// X Old DApp Kit
import { ConnectWallet } from '@vechain/dapp-kit-react';

// ✅ New VeChain Kit
import { WalletButton } from '@vechain/vechain-kit';
```

3. Clean Installation

After removing old packages:

```bash
rm -rf node_modules package-lock.json
npm install
```

**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


# 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

#### [Chakra Conflicts](#chakra-conflicts)

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

#### [CSS Framework Conflicts](#css-framework-conflicts)

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

***

**Can't find your issue?** Search our [GitHub Issues](https://github.com/vechain/vechain-kit/issues) or ask on [Discord](https://discord.com/invite/vechain).


# 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
* 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:

```bash
yarn add @chakra-ui/react@2.8.2
```

#### Setup ChakraProvider

Wrap your app with `ChakraProvider` and include `ColorModeScript`:

<figure><img src="/files/RQtF2eMoL2yrebXEFca3" alt=""><figcaption><p>One common issue, solvable by installing Chakra and defining &#x3C;ColorModeScript /></p></figcaption></figure>

```bash
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>
);
```

#### Key Requirements

The essential setup:

```bash
<ChakraProvider theme={stargateStakingTheme}>
    <ColorModeScript initialColorMode="dark" />
    <AppWrapper />
</ChakraProvider>
```

#### **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&#x20;

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


# 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

* 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:

```css
/* 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 */
}
```

**Framework-Specific Examples**

**Tailwind CSS**

```bash
@layer vechain-kit, host-app;

@layer host-app {
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
}
```

**Tailwind v4**

```
@layer vechain-kit, host-app;

/* Import Tailwind v4 */
@import "tailwindcss";

/* Your custom styles */
@layer host-app {
    /* Custom overrides */
}
```

Bootstrap

```
@layer vechain-kit, host-app;

@layer host-app {
    @import 'bootstrap/dist/css/bootstrap.css';
}
```

Custom CSS

<pre><code><strong>@layer vechain-kit, host-app;
</strong>
@layer host-app {
    body {
        /* Your body styles */
    }

    .your-components {
        /* Component styles */
    }
}
</code></pre>

Quick Fixes for Common Issues

<pre><code><strong>@layer host-app {
</strong>    /* Fix image borders */
    img {
        border-style: solid !important;
    }

    /* Ensure body styles persist */
    html body {
        background: var(--your-bg);
        font-family: var(--your-font);
    }
}
</code></pre>

**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+


# 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

[Fee Delegation](/vechain-kit-v1.x/vechain-kit/troubleshooting/integration-issues/fee-delegation)

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

#### [Privy Popup Blocking](#privy-popup-blocking)

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

***

**Can't find your issue?** Search our [GitHub Issues](https://github.com/vechain/vechain-kit/issues) or ask on [Discord](https://discord.com/invite/vechain).


# 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
* 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

{% hint style="danger" %}
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.
{% endhint %}

2. **Configure VeChain Kit Fee Delegation**

Simply provide the delegation URL in the VeChainKitProvider:

<pre class="language-javascript"><code class="lang-javascript">&#x3C;VeChainKitProvider
<strong>  feeDelegation={{
</strong>    delegatorUrl: process.env.NEXT_PUBLIC_DELEGATOR_URL,
    delegateAllTransactions: false
  }}
  // ... other config
>
  &#x3C;App />
&#x3C;/VeChainKitProvider>
</code></pre>

3. **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.


# 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:

```javascript
// ✅ 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);
};
```

#### Best Practices

1. **Load Data Early**

<pre class="language-javascript"><code class="lang-javascript">// 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
<strong>    });
</strong>};
</code></pre>

2. **Show Loading States**

```javascript
const MyComponent = () => {
    const { data, isLoading } = useQuery(['requiredData'], fetchData);

    return (
      <button 
        onClick={() => sendTransaction(data)}
        disabled={isLoading}
      >
        {isLoading ? 'Preparing...' : 'Send Transaction'}
      </button>
    );
  };

```

3. **Avoid Async in Click Handlers**

```javascript
// ❌ Avoid
onClick={async () => {
  const result = await someAsyncOperation();
  sendTransaction(result);
}}

// ✅ Better
onClick={() => {
  sendTransaction(preLoadedData);
}}
```

#### **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


# 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.

{% hint style="info" %}
**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
  {% endhint %}

## 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.

<figure><img src="/files/JAv7KAZmnnrlt9BSMiOo" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
With this type of connection, you can have social login in your app without actually paying Privy.
{% endhint %}

## 3) Self-custody wallets

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

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

{% hint style="info" %}
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".
{% endhint %}

{% hint style="success" %}
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:

```typescript
import { useDAppKitWalletModal } from '@vechain/vechain-kit';

export const LoginComponent = () => {
  const { open: openWalletModal } = useDAppKitWalletModal();

  return (
    <Button onClick={openWalletModal}>
        Open only "Connect Wallet"
    </Button>
)}
```

{% endhint %}

{% hint style="warning" %}
When your app is opened inside VeWorld mobile wallet, VeWorld is always enforced as a login choice.
{% endhint %}


# Send Transactions

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.

```typescript
'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,
                }}
            />
        </>
    );
}

```

You can build clauses with some of our available build functions, with our [SDK](https://docs.vechain.org/developer-resources/sdks-and-providers/sdk) or  with [connex](https://docs.vechain.org/developer-resources/sdks-and-providers/connex).

If you want to interact directly with the user's smart account read the [Smart Accounts](/vechain-kit-v1.x/social-login/smart-accounts#multiclause-transactions) section.

{% hint style="warning" %}
**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.

```javascript
// ✅ 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);
};
```

{% endhint %}


# Sign Messages

## Sign Message

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

```typescript
'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={handleSignTypedData}
                isLoading={isTypedDataSignPending}
            >
                Sign Typed Data
            </button>
            {typedDataSignature && (
                <h4>
                    {typedDataSignature}
                </h4>
            )}
        </>
    );
}
```

## Sign Typed Data (EIP712)

Use the `useSignTypedData()` hook to sign structured data.&#x20;

```typescript
'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>
            )}
        </>
    );
}
```

## 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).&#x20;

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.

{% hint style="warning" %}
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**.
{% endhint %}

#### Example usage

{% tabs %}
{% tab title="SignatureWrapperProvider.tsx" %}
Create a provider that will handle the signature verification.

```typescript
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>
    </>
  );
};
```

{% endtab %}

{% tab title="main.tsx" %}
Wrap the app with our new provider

```typescript
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>,
  );
})();

```

{% endtab %}

{% tab title="useSignatureVerification.ts" %}
Handle the signature within a custom hook

```typescript
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 };
};
```

{% endtab %}

{% tab title="Backend Validation" %}
On your backend validate the signature as follows:

```typescript
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",
        }),
    };
}

```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
You should **deprecate** this:

```typescript
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>
}
```

{% endhint %}


# 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.

<figure><img src="/files/ebndMZdnrGu5eMDLG6hZ" alt="" width="322"><figcaption></figcaption></figure>

### Types of Records

Here are some of the most commonly used records:

| Name        | Usage                                                                | Reference                                     | Example                                     |
| ----------- | -------------------------------------------------------------------- | --------------------------------------------- | ------------------------------------------- |
| display     | Preferred capitalization                                             | [ENSIP-5](https://docs.ens.domains/ensip/5)   | Luc.eth                                     |
| avatar      | Avatar or logo (see [Avatars](https://docs.ens.domains/web/avatars)) | [ENSIP-5](https://docs.ens.domains/ensip/5)   | ipfs\://dQw4w9WgXcQ                         |
| description | Description of the name                                              | [ENSIP-5](https://docs.ens.domains/ensip/5)   | DevRel @ ENS Labs                           |
| keywords    | List of comma-separated keywords                                     | [ENSIP-5](https://docs.ens.domains/ensip/5)   | person, ens                                 |
| email       | Email address                                                        | [ENSIP-5](https://docs.ens.domains/ensip/5)   | <luc@ens.domains>                           |
| mail        | Physical mailing address                                             | [ENSIP-5](https://docs.ens.domains/ensip/5)   | V3X HQ                                      |
| notice      | Notice regarding this name                                           | [ENSIP-5](https://docs.ens.domains/ensip/5)   | This is a notice                            |
| location    | Generic location (e.g. "Toronto, Canada")                            | [ENSIP-5](https://docs.ens.domains/ensip/5)   | Breda, NL                                   |
| phone       | Phone number as an E.164 string                                      | [ENSIP-5](https://docs.ens.domains/ensip/5)   | +1 234 567 890                              |
| url         | Website URL                                                          | [ENSIP-5](https://docs.ens.domains/ensip/5)   | [https://ens.domains](https://ens.domains/) |
| header      | Image URL to be used as a header/banner                              | [ENSIP-18](https://docs.ens.domains/ensip/18) | ipfs\://dQw4w9WgXcQ                         |

### 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 <a href="#header-banner-record" id="header-banner-record"></a>

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 <a href="#setting-records" id="setting-records"></a>

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.

{% hint style="info" %}
This is protocol build by ENS domains and supported by veDelegate. Follow [ENS documentation ](https://docs.ens.domains/web/records)for more information.
{% endhint %}

## Keep reading how to integrate with the VeChain Kit

{% content-ref url="/pages/0eRPZP4rlIYU5quJc6va" %}
[vetDomains](/vechain-kit-v1.x/vechain-kit/hooks/vetdomains)
{% endcontent-ref %}

{% content-ref url="/pages/7CHe0v8VZqBxzugUu9Rr" %}
[Profile Card](/vechain-kit-v1.x/vechain-kit/components/profile-card)
{% endcontent-ref %}


# Hooks

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

The hooks in this package provide a standardized way to interact with various blockchain and web services. All hooks are built using [TanStack Query](https://tanstack.com/query) (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:

* `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:

```typescript
const queryClient = useQueryClient();
// Invalidate all blockchain queries
queryClient.invalidateQueries({ queryKey: ['VECHAIN_KIT'] });
// Invalidate specific query
queryClient.invalidateQueries({ queryKey: ['VECHAIN_KIT', 'CURRENT_BLOCK'] });
```

### 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.


# 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

```typescript
import { useWallet } from "@vechain/vechain-kit"; 

function MyComponent = () => {
    const {
        account,
        connectedWallet,
        smartAccount,
        privyUser,
        connection,
        disconnect
    } = useWallet();
    
    return <></>
}


```

### Types

{% tabs %}
{% tab title="Wallet" %}

```typescript
export type Wallet = {
    address: string;
    domain?: string;
    image: string;
} | null;
```

{% endtab %}

{% tab title="SmartAccount" %}

```typescript
export type SmartAccount = Wallet & {
    isDeployed: boolean;
    isActive: boolean;
    version: string | null;
};
```

{% endtab %}

{% tab title="Connection source" %}

```typescript
export type ConnectionSource = {
    type: 'privy' | 'wallet' | 'privy-cross-app';
    displayName: string;
};
```

{% endtab %}

{% tab title="PrivyUser" %}

```typescript
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;
}
```

{% endtab %}
{% endtabs %}

### 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 [here](#smartaccount).
* `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.


# Smart Account

### Hooks

The hooks offer tools for efficient smart account management:

* **`useGetAccountAddress`**: Retrieves the smart account's address linked to the wallet.
* **`useGetAccountVersion`**: Retrieces the smart account's version even if the account is not deployed yet
* **`useSmartAccountVersion`**: Retrieves the smart account's version, but only if it is already deployed, and it will revert for v1 smart accounts (because function was not implemented in that smart contract)
* **`useSmartAccount`**: Retrieves the the account of an owner, returning additional information
* **`useAccountImplementationAddress`**: Returns the implementation address of a specific version; this hook is useful when upgrading the smart account of the user
* **`useCurrentAccountImplementationVersion`**: Returns the latest (and currently used) implementation version of the smart accounts used by the factory
* **`useUpgradeRequired`**: Returns if a smart account needs an upgrade (even if it's not yet deployed)
* **`useUpgradeRequiredForAccount`**: As above but only if the account is not yet deployed
* **`useIsSmartAccountDeployed`**: Verifies the deployment status of the smart account.
* **`useHasV1SmartAccount`**: Checks for a legacy version's existence.
* **`useUpgradeSmartAccount`**: Hook to create a transaction to upgrade the smart account to a specific version

### Usage

```typescript
import {
    useSmartAccountVersion,
    useGetSmartAccountAddress,
    useIsSmartAccountDeployed,
    useSmartAccountNeedsUpgrade,
    useWallet,
    useSmartAccountImplementationAddress,
    useUpgradeSmartAccountVersion,
    useHasV1SmartAccount,
} from '@vechain/vechain-kit';

// connectedWallet is the owner of the smart account, can be the Privy embedded wallet or VeWorld
const { connectedWallet } = useWallet();

const { data: smartAccountAddress } = useGetSmartAccountAddress(
    connectedWallet?.address,
);

const { data: upgradeRequired } = useUpgradeRequired(
    smartAccountAddress,
    ownerAddress: connectedWallet?.address ?? "",
    version: 3
);

const { data: smartAccountDeployed } =
    useIsSmartAccountDeployed(smartAccountAddress);

const { data: currentSmartAccountVersion } =
    useSmartAccountVersion(smartAccountAddress);

const { data: smartAccountNeedsUpgrade } = useSmartAccountNeedsUpgrade(
    smartAccountAddress,
    3,
);

const { data: smartAccountImplementationVersion } =
    useCurrentAccountImplementationVersion();

const { data: smartAccountImplementationAddress } =
    useSmartAccountImplementationAddress(3);

// Use the new hook for upgrading
const {
    sendTransaction: upgradeSmartAccount,
    isTransactionPending,
    error: upgradeError,
} = useUpgradeSmartAccount({
    smartAccountAddress: smartAccountAddress ?? '',
    targetVersion: 3,
    onSuccess: () => {
        console.log('Smart Account upgraded successfully!');
    },
    onError: () => {
        console.log('Error upgrading Smart Account.');
    },
});

console.log(
    'Smart Account',
    smartAccountAddress,
    'deployed',
    smartAccountDeployed,
    'version',
    currentSmartAccountVersion,
    'upgradeRequired',
    upgradeRequired,
    'current implementation version',
    smartAccountImplementationVersion,
    'v3 implementation address',
    smartAccountImplementationAddress,
);
```


# Blockchain Hooks

### `useCurrentBlock()`

Fetches the current block from the blockchain with automatic updates.

```typescript
const { data, isLoading, error } = useCurrentBlock();
```

Features:

* Auto-refreshes every 10 seconds
* Caches data for 1 minute
* Returns the latest block information as Connex.Thor.Block

Example:

```typescript
function BlockInfo() {
  const { data: block } = useCurrentBlock();
  
  return <div>Current Block: {block?.number}</div>;
}
```

### `useTxReceipt()`

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

```typescript
const { data, isLoading, error } = useTxReceipt(txId, blockTimeout);
```

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 (Connex.Thor.Transaction.Receipt)
* isLoading: Boolean indicating if the receipt is being fetched
* error: Error object if the operation fails

Example:

```typescript
function TransactionStatus({ txId }) {
  const { data: receipt, isLoading } = useTxReceipt(txId);
  
  if (isLoading) return <div>Loading...</div>;
  return <div>Transaction Status: {receipt?.reverted ? 'Failed' : 'Success'}</div>;
}
```

## Utility Functions

### **`getEvents()`**

Fetches events from the blockchain based on specified criteria.

```typescript
const events = await getEvents({
  nodeUrl,
  thor,
  filterCriteria,
  order: 'asc',
  offset: 0,
  limit: 1000,
  from: 0,
  to: undefined
});
```

### **`getAllEvents()`**

Iteratively fetches all events matching the criteria, handling pagination automatically.

```typescript
const allEvents = await getAllEvents({
  nodeUrl,
  thor,
  filterCriteria,
  order: 'asc',
  from: 0,
  to: undefined
});
```

Parameters for getEvents/getAllEvents:

* nodeUrl: VeChain node URL
* thor: Thor client instance
* filterCriteria: Array of event filter criteria
* order: Sort order ('asc' or 'desc')
* from: Starting block number
* to: Ending block number
* offset: (getEvents only) Number of events to skip
* limit: (getEvents only) Maximum number of events to return

These hooks and functions are designed to work with the VeChain blockchain and require the VeChain Kit context to be properly set up in your application.


# Oracle

### `useGetTokenUsdPrice`

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

#### Usage

```typescript
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>;
}
```

### 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                                     |
| --------- | ------------- | ----------------------------------------------- |
| 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.   |

### 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

```typescript
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>
  );
}
```


# VeBetterDAO

## VeBetterDAO Hooks

The hooks provide tools for interacting with VeBetterDAO's smart contracts and features:

### Galaxy Member Hooks

* `useB3trDonated`: Fetches the amount of B3TR tokens donated for a given token ID
* `useB3trToUpgrade`: Retrieves the amount of B3TR tokens required to upgrade a specific token
* `useB3trToUpgradeToLevel`: Gets the amount of B3TR needed to upgrade to a specific level
* `useGMBaseUri`: Retrieves the base URI for the Galaxy Member NFT metadata
* `useGMMaxLevel`: Fetches the maximum level possible for Galaxy Member NFTs
* `useGMbalance`: Gets the number of GM NFTs owned by an address
* `useGetNodeIdAttached`: Retrieves the Vechain Node Token ID attached to a given GM Token ID
* `useGetTokenIdAttachedToNode`: Gets the GM Token ID attached to a given Vechain Node ID
* `useGetTokensInfoByOwner`: Fetches token information for a specific owner with infinite scrolling support
* `useIsGMclaimable`: Determines if a user can claim a GM NFT
* `useLevelMultiplier`: Gets the reward multiplier for a specific GM level
* `useLevelOfToken`: Retrieves the level of a specified token
* `useParticipatedInGovernance`: Checks if an address has participated in governance
* `useSelectedGmNft`: Comprehensive hook for retrieving data related to a Galaxy Member NFT
* `useSelectedTokenId`: Gets the selected token ID for the selected galaxy member
* `useTokenIdByAccount`: Retrieves the token ID for an address given an index

### Node Management Hooks

* `useGetNodeManager`: Gets the address of the user managing a node ID (endorsement) either through ownership or delegation

### Rewards Hooks

* `useVotingRewards`: Manages voting rewards functionality
* `useVotingRoundReward`: Handles rewards for specific voting rounds

### Usage Example

```typescript
// Example usage of VeBetterDAO hooks
import {
    useGMbalance,
    useIsGMclaimable,
    useSelectedGmNft,
    useGetNodeManager,
    useParticipatedInGovernance,
} from '@vechain/vechain-kit';

const ExampleComponent = () => {
    const userAddress = "0x..."; // User's wallet address

    // Check GM NFT balance
    const { data: gmBalance } = useGMbalance(userAddress);

    // Check if user can claim a GM NFT
    const { isClaimable, isOwned } = useIsGMclaimable(userAddress);

    // Get comprehensive GM NFT data
    const {
        gmId,
        gmImage,
        gmName,
        gmLevel,
        gmRewardMultiplier,
        nextLevelGMRewardMultiplier,
        isGMLoading,
        isGMOwned,
        b3trToUpgradeGMToNextLevel,
        isEnoughBalanceToUpgradeGM,
        missingB3trToUpgrade,
        attachedNodeId,
        isXNodeAttachedToGM,
        maxGmLevel,
        isMaxGmLevelReached,
    } = useSelectedGmNft(userAddress);

    // Get node manager
    const { data: nodeManager } = useGetNodeManager(nodeId);

    // Check governance participation
    const { data: hasParticipated } = useParticipatedInGovernance(userAddress);

    console.log(
        'User GM NFTs:',
        gmBalance,
        'Can claim:',
        isClaimable,
        'Current GM Level:',
        gmLevel,
        'Node Manager:',
        nodeManager,
        'Has participated:',
        hasParticipated
    );

    return (
        // Your component JSX here
    );
};

export default ExampleComponent;
```


# veDelegate

## VeDelegate Hooks

The hooks provide tools for interacting with VeDelegate token and functionality:

### Token Hooks

* `useGetVeDelegateBalance`: Retrieves the VeDelegate token balance for a given address

### Usage Example

```typescript
// Example usage of VeDelegate hooks
import { useGetVeDelegateBalance } from '@vechain/vechain-kit';

const ExampleComponent = () => {
    const userAddress = "0x..."; // User's wallet address

    // Get VeDelegate token balance
    const { data: veDelegateBalance, isLoading } = useGetVeDelegateBalance(userAddress);

    console.log(
        'VeDelegate Balance:',
        veDelegateBalance,
        'Loading:',
        isLoading
    );

    return (
        // Your component JSX here
    );
};

export default ExampleComponent;

/*
Note: The VeDelegate balance query will only be enabled when:
- A valid thor connection is available
- A valid address is provided
- A valid network type is configured

The balance is returned as a string representing the token amount.
*/
```


# 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.
* `useGetResolverAddress`: Gets the resolver contract address for a domain
* `useUpdateTextRecord`: Updates text records for a domain with transaction handling

### Subdomain Management Hooks

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

### Usage Example

```typescript
/**
 * 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
*/
```


# 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

```typescript
// 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)
*/
```


# Indexer

## Indexer Hooks

The hooks provide tools for interacting with the B3TR indexer service, offering sustainability and voting data:

### Sustainability Hooks

* `useSustainabilityActions`: Fetches sustainability actions with pagination for a user or app, returning detailed impact data including:
  * Environmental metrics (carbon, water, energy)
  * Waste metrics (mass, items, reduction)
  * Social impact (biodiversity, people)
  * Resource conservation (timber, plastic)
  * Educational impact (learning time)
  * Activity metrics (trees planted, calories burned)
  * Energy metrics (clean energy production)
  * Health metrics (sleep quality)

### Voting & Allocation Hooks

* `useRoundAppVotes`: Retrieves voting results for a specific round, including:
  * Number of voters
  * Total votes cast
  * App-specific voting data
  * Round statistics

### Usage Example

```typescript
// Example usage of Indexer hooks
import { 
    useSustainabilityActions,
    useRoundAppVotes 
} from '@vechain/vechain-kit';

const ExampleComponent = () => {
    // Get sustainability actions with pagination
    const { 
        data: sustainabilityData,
        hasNextPage,
        fetchNextPage,
        isLoading: isLoadingActions 
    } = useSustainabilityActions({
        wallet: "0x...", // Optional: filter by wallet
        appId: "app_id", // Optional: filter by app
        direction: "desc", // Optional: sort direction
        limit: 10 // Optional: items per page
    });

    // Get voting results for a specific round
    const { 
        data: roundVotes,
        isLoading: isLoadingVotes 
    } = useRoundAppVotes({
        roundId: 1
    });

    // Handle loading more sustainability actions
    const handleLoadMore = () => {
        if (hasNextPage) {
            fetchNextPage();
        }
    };

    console.log(
        'Sustainability Actions:', sustainabilityData?.pages,
        'Has More Pages:', hasNextPage,
        'Round Votes:', roundVotes
    );

    return (
        // Your component JSX here
    );
};

export default ExampleComponent;

/*
Note: These hooks require:
- A valid indexer URL configuration
- Appropriate network settings
- Valid input parameters (wallet address, appId, or roundId)

Sustainability data includes:
- Environmental metrics (carbon, water, energy)
- Waste metrics (mass, items, reduction)
- Social impact (biodiversity, people)
- Resource conservation (timber, plastic)
- Educational impact (learning time)
- Activity metrics (trees planted, calories burned)
- Energy metrics (clean energy production)
- Health metrics (sleep quality)

Round votes data includes:
- Number of voters
- Total votes cast
- App-specific voting data
- Round statistics
*/
```


# Ipfs

## IPFS Hooks

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

### Image Hooks

* `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

```typescript
// 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
*/
```


# NFTs

## NFT Hooks

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

### NFT Data Hooks

* `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

```typescript
// 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

```typescript
// 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;
    }[];
}
*/
```


# 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

```typescript
// 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: () => Promise<void>;
    isTransactionPending: boolean;
    isWaitingForWalletConfirmation: boolean;
    txReceipt: Connex.Thor.Transaction.Receipt | 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;
}
```

### Usage example

```typescript
// 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)
*/
```


# Signing

## Signing Hooks

The hooks provide tools for signing messages and typed data on VeChain:

### Core Signing Hooks

* `useSignMessage`: Hook for signing plain text messages, supporting both Privy and VeChain wallets
* `useSignTypedData`: Hook for signing typed data (EIP-712), supporting both Privy and VeChain wallets

### Types

```typescript
// Signing Types
interface SignTypedDataParams {
    domain: {
        name?: string;
        version?: string;
        chainId?: number;
        verifyingContract?: string;
        salt?: string;
    };
    types: Record<string, Array<{ name: string; type: string }>>;
    message: Record<string, any>;
}

interface UseSignMessageReturnValue {
    signMessage: (message: string) => Promise<string>;
    isSigningPending: boolean;
    signature: string | null;
    error: Error | null;
    reset: () => void;
}

interface UseSignTypedDataReturnValue {
    signTypedData: (data: SignTypedDataParams) => Promise<string>;
    isSigningPending: boolean;
    signature: string | null;
    error: Error | null;
    reset: () => void;
}
```

### Usage example

```typescript
// Example usage of Signing hooks
import { useSignMessage, useSignTypedData } from '@vechain/vechain-kit';

const ExampleComponent = () => {
    // Example of signing a message
    const { 
        signMessage,
        isSigningPending: isMessagePending,
        signature: messageSignature,
        error: messageError,
        reset: resetMessage
    } = useSignMessage();

    // Example of signing typed data
    const {
        signTypedData,
        isSigningPending: isTypedDataPending,
        signature: typedDataSignature,
        error: typedDataError,
        reset: resetTypedData
    } = useSignTypedData();

    const handleMessageSign = async () => {
        try {
            const signature = await signMessage("Hello VeChain!");
            console.log("Message signature:", signature);
        } catch (error) {
            console.error("Message signing error:", error);
        }
    };

    const handleTypedDataSign = async () => {
        try {
            const typedData = {
                domain: {
                    name: 'MyDApp',
                    version: '1',
                    chainId: 1,
                    verifyingContract: '0x...'
                },
                types: {
                    Person: [
                        { name: 'name', type: 'string' },
                        { name: 'age', type: 'uint256' }
                    ]
                },
                message: {
                    name: 'John Doe',
                    age: 30
                }
            };
            
            const signature = await signTypedData(typedData);
            console.log("Typed data signature:", signature);
        } catch (error) {
            console.error("Typed data signing error:", error);
        }
    };

    return (
        <div>
            <button 
                onClick={handleMessageSign} 
                disabled={isMessagePending}
            >
                Sign Message
            </button>
            <button 
                onClick={handleTypedDataSign} 
                disabled={isTypedDataPending}
            >
                Sign Typed Data
            </button>

            {/* Display signatures */}
            {messageSignature && (
                <div>Message Signature: {messageSignature}</div>
            )}
            {typedDataSignature && (
                <div>Typed Data Signature: {typedDataSignature}</div>
            )}

            {/* Display errors */}
            {messageError && (
                <div>Message Error: {messageError.message}</div>
            )}
            {typedDataError && (
                <div>Typed Data Error: {typedDataError.message}</div>
            )}

            {/* Reset buttons */}
            <button onClick={resetMessage}>Reset Message Signing</button>
            <button onClick={resetTypedData}>Reset Typed Data Signing</button>
        </div>
    );
};

export default ExampleComponent;

/*
Note: These hooks require:
- Valid wallet connection (Privy or VeChain)
- For typed data signing:
  - Valid EIP-712 typed data structure
  - Proper domain specification
  - Correct types definition
*/
```


# Login

## Login Hooks

The hooks provide authentication methods for VeChain applications:

### Authentication Hooks

* `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

```typescript
// 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>;
}
```

### Usage example

```typescript
// 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
*/
```


# Components

## Component Overview

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

* **WalletButton**: Dynamically triggers either a login or account modal based on the user's connection status.
* **ProfileCard**: Displays essential details, including an avatar, description, and social links linked to a user's address.
* **Transaction Visibility**:
  * **TransactionModal**
  * **TransactionToast**
* **AccountModal**: Allows users to explore specific sections as needed.
* **DAppKitWalletButton**: Provides a focused interface for selecting wallet connection options.

These components collectively enhance user interaction and streamline.

Head over the [VeChain Kit homepage](https://vechainkit.vechain.org/) to see all the components in action.




---

[Next Page](/llms-full.txt/1)

