Skip to content

Vue.js Implementation Examples

The PwnDAO SDK seamlessly integrates with Vue.js applications using Vue Query. Here are the key implementation examples:

1. Strategy Hook Setup

Create a reusable composable for fetching strategy data:

// composables/use-strategy.ts
import { getStrategy } from "@pwndao/v1-core"
import { useQuery } from "@tanstack/vue-query"
 
export const useStrategy = (strategyId: string) => {
    return useQuery({
        queryKey: ['strategy', strategyId],
        queryFn: ({ queryKey }) => getStrategy(queryKey[1]),
        enabled: !!strategyId,
    })
}

2. Using the Strategy Hook

Example of a component using the strategy hook:

<script setup lang="ts">
import { useStrategy } from '@/composables/use-strategy'
 
// Use the strategy hook with a strategy ID
const { data: strategy, isLoading, error } = useStrategy('your-strategy-id')
</script>
 
<template>
  <div>
    <div v-if="isLoading">Loading strategy...</div>
    <div v-else-if="error">Error loading strategy</div>
    <div v-else>
      <h1>{{ strategy.name }}</h1>
      <div class="strategy-details">
        <div>
          <h3>Lending Stats</h3>
          <div>Total Committed: {{ formatUnits(strategy.lendingStats.totalCommittedAmount, 18) }} USD</div>
          <div>Total Available: {{ formatUnits(strategy.lendingStats.totalAvailableAmount, 18) }} USD</div>
        </div>
        <div>
          <h3>Terms</h3>
          <div>APR: {{ strategy.terms.apr[getPairKey(strategy)] }}%</div>
          <div>LTV: {{ strategy.terms.ltv[getPairKey(strategy)] }}%</div>
          <div>Duration: {{ strategy.terms.durationDays }} days</div>
        </div>
      </div>
    </div>
  </div>
</template>
 
<script lang="ts">
import { formatUnits } from 'viem'
 
const getPairKey = (strategy) => {
  const firstCollateralAsset = strategy.terms.collateralAssets[0]
  const firstCreditAsset = strategy.terms.creditAssets[0]
  return `${firstCollateralAsset.address}/${firstCollateralAsset.chainId}-${firstCreditAsset.address}/${firstCreditAsset.chainId}`
}
</script>

3. Creating Strategy Commitments

Example of a component for creating strategy commitments:

<script setup lang="ts">
import { ref } from 'vue';
import { useAccount, useConfig } from '@wagmi/vue';
import { sepolia } from 'wagmi/chains';
import { useMakeProposals, useUserWithNonce } from '@pwndao/sdk-v1-vue';
import { createElasticProposals } from '@pwndao/v1-core';
import type { Strategy } from '@pwndao/v1-core';
 
const props = defineProps<{
  strategy: Strategy;
}>();
 
const { address, isConnected } = useAccount();
const config = useConfig();
 
// Form state
const creditAmount = ref('100');
const errorMessage = ref<string | null>(null);
 
const { userWithNonce: user } = useUserWithNonce([sepolia.id]);
const {
  mutateAsync: makeProposal,
  isPending: isLoading,
  isSuccess,
  error,
  data: txHash,
} = useMakeProposals(user);
 
// Handle form submission
const handleSubmit = async (e: Event) => {
  e.preventDefault();
  errorMessage.value = null;
 
  if (!isConnected.value || !address.value || !user.value) {
    return;
  }
 
  try {
    // Create proposals with proper parameters
    const proposalsToCreate = createElasticProposals(
      props.strategy,
      address.value,
      creditAmount.value,
      config,
    );
 
    const res = await makeProposal(proposalsToCreate);
    console.log("Proposals created successfully:", res);
  } catch (err) {
    console.error("Error creating commitment:", err);
    errorMessage.value = err instanceof Error ? err.message : "An unknown error occurred";
  }
};
</script>
 
<template>
  <div>
    <form @submit="handleSubmit" class="space-y-4">
      <!-- Credit Amount Input -->
      <div>
        <label>Credit Amount</label>
        <input
          v-model="creditAmount"
          type="text"
          placeholder="Enter amount"
          required
        />
      </div>
 
      <!-- Strategy Terms Display -->
      <div>
        <h3>Strategy Terms</h3>
        <div>
          <div>APR: {{ strategy.terms.apr[Object.keys(strategy.terms.apr)[0]] }}%</div>
          <div>LTV: {{ strategy.terms.ltv[Object.keys(strategy.terms.ltv)[0]] }}%</div>
          <div>Duration: {{ strategy.terms.durationDays }} days</div>
          <div>Expiration: {{ strategy.terms.expirationDays }} days</div>
        </div>
      </div>
 
      <!-- Submit Button -->
      <button
        type="submit"
        :disabled="isLoading || (!isConnected && !address)"
      >
        {{ isLoading
          ? 'Creating Commitment...'
          : !isConnected
            ? 'Connect Wallet to Continue'
            : 'Create Commitment' }}
      </button>
    </form>
 
    <!-- Transaction Status -->
    <div v-if="isLoading" class="mt-6">
      <p>Transaction in progress...</p>
    </div>
 
    <div v-if="isSuccess" class="mt-6">
      <p>Commitment created successfully!</p>
      <p v-if="txHash">
        Transaction Hash:
        <a
          :href="`https://sepolia.etherscan.io/tx/${txHash}`"
          target="_blank"
          rel="noopener noreferrer"
        >
          {{ JSON.stringify(txHash) }}
        </a>
      </p>
    </div>
 
    <div v-if="error || errorMessage" class="mt-6">
      <p>Error creating commitment:</p>
      <p>{{ errorMessage || error?.message || String(error) }}</p>
    </div>
  </div>
</template>

4. Custom Authentication with useUserNonces

When implementing World App's MiniKit for authentication instead of Wagmi, you can use the useUserNonces composable to manage user nonces across different chains. This is particularly useful when building mini apps for World App:

<script setup lang="ts">
import { useUserNonces } from '@pwndao/sdk-v1-vue';
import { SupportedChain } from '@pwndao/sdk-core';
import { MiniKit } from '@worldcoin/minikit-js';
import { ref, onMounted } from 'vue';
 
// Reactive state for address
const address = ref<string>();
const chainIds = ref([SupportedChain.World, SupportedChain.Sepolia]);
const isMinikitAvailable = ref(false);
 
// Initialize MiniKit
onMounted(() => {
  MiniKit.install();
  isMinikitAvailable.value = MiniKit.isInstalled();
});
 
// Handle World App authentication
const handleAuth = async () => {
  if (!isMinikitAvailable.value) {
    console.error('Please open this app in World App');
    return;
  }
 
  try {
    const response = await MiniKit.walletAuth();
    address.value = response.address;
  } catch (err) {
    console.error('Authentication failed:', err);
  }
};
 
const { data: userWithNonces, isLoading, error } = useUserNonces(
  address,
  chainIds
);
</script>
 
<template>
  <div>
    <div v-if="!isMinikitAvailable">
      Please open this app in World App
    </div>
    <div v-else-if="!address">
      <button @click="handleAuth">
        Authenticate with World App
      </button>
    </div>
    <div v-else>
      <div v-if="isLoading">Loading user nonces...</div>
      <div v-else-if="error">Error: {{ error.message }}</div>
      <div v-else>
        <h2>User Nonces</h2>
        <div>Connected Address: {{ address }}</div>
        <div v-if="userWithNonces">
          <div v-for="chainId in chainIds" :key="chainId">
            Chain {{ chainId }}: Nonce {{ userWithNonces.nonces[chainId] }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

The useUserNonces composable is designed for:

  • Building mini apps for World App using MiniKit
  • Tracking user nonces across multiple chains
  • Custom authentication flows outside of Wagmi
  • Managing proposal sequences with proper nonce tracking

Key features:

  • Reactive nonce tracking for specified chains
  • Automatic nonce synchronization
  • TanStack Query integration for efficient caching
  • Type-safe implementation with full TypeScript support
  • Seamless integration with World App's MiniKit

For more information about MiniKit integration, visit the World App Mini Apps documentation.

Important Considerations

  1. Multi-Proposal Support
    • Uses createElasticProposals to generate multiple proposals
    • Handles batch proposal creation in a single transaction
    • Manages nonces automatically across chains
  2. Error Handling
    • Implements comprehensive error handling
    • Shows user-friendly error messages
    • Handles both transaction and validation errors
  3. Transaction Status
    • Displays loading state during transaction
    • Shows success message with transaction hash
    • Provides etherscan link for transaction tracking
  4. Type Safety
    • Uses TypeScript for type safety
    • Leverages SDK types for consistency
    • Provides proper type inference
  5. Vue Composition API
    • Uses Vue 3 Composition API
    • Implements reactive state management
    • Follows Vue best practices for component organization

For more detailed examples and advanced usage patterns, check out our Vue Example App.