Skip to main content

Vue Quick Start

Build your first confidential dApp with Vue 3 and FHEVM SDK in under 10 minutes.

Installation

First, install the required packages:

pnpm install @fhevmsdk/core @fhevmsdk/vue vue @wagmi/vue @tanstack/vue-query viem

Setup Wagmi for Vue

Configure Wagmi for Vue in your application:

// config/wagmi.ts
import { http, createConfig } from '@wagmi/vue'
import { sepolia } from '@wagmi/vue/chains'
import { injected } from '@wagmi/vue/connectors'

export const config = createConfig({
chains: [sepolia],
connectors: [injected()],
transports: {
[sepolia.id]: http(),
},
})

Setup FHEVM in Your App

In your main.ts, set up the FHEVM context:

// main.ts
import { createApp } from 'vue'
import { WagmiPlugin } from '@wagmi/vue'
import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query'
import { setupFHEVM, FHEVMContextKey } from '@fhevmsdk/vue'
import { config } from './config/wagmi'
import App from './App.vue'

const queryClient = new QueryClient()

// Initialize FHEVM
const fhevmContext = setupFHEVM({ network: 'sepolia' })

const app = createApp(App)

app.use(WagmiPlugin, { config })
app.use(VueQueryPlugin, { queryClient })
app.provide(FHEVMContextKey, fhevmContext)

app.mount('#app')

Use FHEVM Composables

Now you can use FHEVM composables in your components:

Check FHEVM Status

<script setup lang="ts">
import { useFHEVM } from '@fhevmsdk/vue'

const { isReady, isLoading, error } = useFHEVM()
</script>

<template>
<div>
<div v-if="isLoading">Initializing FHEVM...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<div v-else-if="!isReady">FHEVM not ready</div>
<div v-else>FHEVM is ready!</div>
</div>
</template>

Encrypt Data

<script setup lang="ts">
import { ref } from 'vue'
import { useEncrypt } from '@fhevmsdk/vue'

const amount = ref('')
const encrypted = ref<string>()
const { encrypt } = useEncrypt()

const handleEncrypt = async () => {
const result = await encrypt.uint64({
value: BigInt(amount.value),
contractAddress: '0x...',
userAddress: '0x...',
})
encrypted.value = result.data
}
</script>

<template>
<div>
<input v-model="amount" placeholder="Amount" />
<button @click="handleEncrypt">Encrypt</button>
<p v-if="encrypted">Encrypted: {{ encrypted }}</p>
</div>
</template>

View Confidential Balance

<script setup lang="ts">
import { useConfidentialBalance } from '@fhevmsdk/vue'
import { tokenABI } from './abi'

const { decryptedBalance, revealBalance, isRevealing } = useConfidentialBalance({
contractAddress: '0x1234567890123456789012345678901234567890',
abi: tokenABI,
})
</script>

<template>
<div>
<p v-if="decryptedBalance">Balance: {{ decryptedBalance }}</p>
<button v-else @click="revealBalance" :disabled="isRevealing">
{{ isRevealing ? 'Revealing...' : 'Reveal Balance' }}
</button>
</div>
</template>

Transfer Confidential Tokens

<script setup lang="ts">
import { ref } from 'vue'
import { useConfidentialTransfer } from '@fhevmsdk/vue'
import { tokenABI } from './abi'

const to = ref('')
const amount = ref('')

const { transfer, isLoading, isSuccess, error } = useConfidentialTransfer({
contractAddress: '0x1234567890123456789012345678901234567890',
abi: tokenABI,
})

const handleTransfer = async () => {
await transfer({
to: to.value,
amount: amount.value,
})
}
</script>

<template>
<div>
<input v-model="to" placeholder="Recipient address" />
<input v-model="amount" placeholder="Amount" />
<button @click="handleTransfer" :disabled="isLoading">
{{ isLoading ? 'Transferring...' : 'Transfer' }}
</button>
<p v-if="isSuccess">Transfer successful!</p>
<p v-if="error">Error: {{ error.message }}</p>
</div>
</template>

Complete Example

Here's a complete working example:

App.vue

<script setup lang="ts">
import { useFHEVM } from '@fhevmsdk/vue'
import { useAccount, useConnect } from '@wagmi/vue'
import ConnectWallet from './components/ConnectWallet.vue'
import ConfidentialToken from './components/ConfidentialToken.vue'

const { isReady } = useFHEVM()
const { address } = useAccount()
</script>

<template>
<div class="app">
<h1>My Confidential dApp</h1>
<ConnectWallet />
<div v-if="address && isReady">
<ConfidentialToken />
</div>
</div>
</template>

<style scoped>
.app {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
</style>

ConnectWallet.vue

<script setup lang="ts">
import { useAccount, useConnect, useDisconnect } from '@wagmi/vue'

const { connectors, connect } = useConnect()
const { disconnect } = useDisconnect()
const { address } = useAccount()
</script>

<template>
<div>
<button v-if="!address" @click="connect({ connector: connectors[0] })">
Connect Wallet
</button>
<div v-else>
<p>Connected: {{ address }}</p>
<button @click="disconnect()">Disconnect</button>
</div>
</div>
</template>

ConfidentialToken.vue

<script setup lang="ts">
import { useConfidentialBalance } from '@fhevmsdk/vue'
import { tokenABI } from '../abi'

const { decryptedBalance, revealBalance, isRevealing } = useConfidentialBalance({
contractAddress: '0x...',
abi: tokenABI,
})
</script>

<template>
<div>
<h2>Confidential Token</h2>
<div v-if="decryptedBalance">
<p>Balance: {{ decryptedBalance }}</p>
</div>
<button v-else @click="revealBalance" :disabled="isRevealing">
{{ isRevealing ? 'Revealing...' : 'Reveal Balance' }}
</button>
</div>
</template>

TypeScript Support

For optimal TypeScript support, update your tsconfig.json:

{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"jsx": "preserve",
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts", "src/**/*.vue"]
}

And configure Vite for proper resolution in vite.config.ts:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': '/src',
},
},
})

Next Steps

Common Patterns

Error Handling

<script setup lang="ts">
import { useFHEVM } from '@fhevmsdk/vue'

const { error } = useFHEVM()

const retry = () => {
window.location.reload()
}
</script>

<template>
<div v-if="error" class="error">
<h3>FHEVM Error</h3>
<p>{{ error.message }}</p>
<button @click="retry">Retry</button>
</div>
<slot v-else />
</template>

Loading States

<script setup lang="ts">
import { useFHEVM } from '@fhevmsdk/vue'

const { isReady, isLoading } = useFHEVM()
</script>

<template>
<div>
<div v-if="isLoading" class="spinner">Loading...</div>
<div v-else-if="!isReady">Please wait...</div>
<slot v-else />
</div>
</template>

Reactive State

<script setup lang="ts">
import { computed, watchEffect } from 'vue'
import { useConfidentialBalance } from '@fhevmsdk/vue'

const { decryptedBalance } = useConfidentialBalance({ ... })

// Computed property based on decrypted balance
const formattedBalance = computed(() => {
if (!decryptedBalance.value) return 'Hidden'
return `${decryptedBalance.value} tokens`
})

// Watch for changes
watchEffect(() => {
if (decryptedBalance.value) {
console.log('Balance updated:', decryptedBalance.value)
}
})
</script>

<template>
<div>{{ formattedBalance }}</div>
</template>

Troubleshooting

See the Troubleshooting Guide for common issues and solutions.