Wagmi ו-viem: אינטגרציית בלוקצ'יין מהירה לאתרי React

עבודה עם בלוקצ'יין באפליקציות React הופכת פעמים רבות לקרב מול נתונים מיושנים ובקשות מיותרות. אנו משלבים את מחסנית wagmi ו-viem המודרנית, מה שהופך אינטראקציות עם חוזים למהירות וצפויות. הצוות שלנו מספק את הפרויקט במפתח מלא—מהגדרת הלקוח ועד לטיפוסי החוזים—ומבטיח ביצועים יציבים ותמיכה מתמשכת.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
Wagmi ו-viem: אינטגרציית בלוקצ'יין מהירה לאתרי React
בינוני
~3-5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

תארו לעצמכם שאפליקציית React שלכם משתמשת ב-ethers.js כדי לתקשר עם חוזים. כשהמשתמש מחליף רשת, הנתונים לא מתעדכנים, ובקשות N+1 ל-RPC חונקות את האפליקציה. נתקלנו בזה פעמים רבות — חוסר התאמה בהידרציה ב-Next.js, דליפות זיכרון ממצב משתנה של ethers.js. הפתרון הוא שילוב wagmi + viem. הניסיון שלנו בפיתוח Web3 עולה על 5 שנים, ויישמנו פתרון זה ביותר מ-10 פרויקטי DeFi. המעבר ל-wagmi/viem מפחית את נפח הקוד פי 2–3, מוריד את ה-TTFB ב-40%, ומפחית את עלויות בקשות ה-RPC הודות למטמון אוטומטי.

למה ethers.js מאט את הביצועים

ethers.js היא ספרייה מונחית מחלקות עם מצב משתנה. כל useReadContract יוצר אובייקט המקושר לספק. ב-React זה בעייתי: כשהרשת או החשבון משתנים, יש ליצור את האובייקט מחדש, אחרת הנתונים לא מתעדכנים. Viem בנוי על פונקציות וקליינטים בלתי משתנים. Wagmi מנהל אוטומטית את מחזור החיים של הקליינט — ה-hooks מתעדכנים בכל שינוי בארנק. לפי הסטטיסטיקות שלנו, המעבר ל-wagmi/viem מפחית את נפח הקוד פי 2–3 ומוריד את ה-TTFB ב-40%.

השוואה: ethers.js לעומת wagmi/viem

קריטריון ethers.js wagmi + viem
מצב משתנה, אובייקטים חיים בקונטקסט בלתי משתנה, אין צורך ביצירה מחדש
אינטגרציית React ידנית באמצעות useState/useEffect hooks מובנים עם מטמון
עדכון בעת החלפת רשת צריך להירשם לאירועים אחזור אוטומטי באמצעות watch
טיפוסים חלש, המרות ידניות טיפוסים מלאים באמצעות @wagmi/cli
ביצועים אחזור גבוה יותר עקב מחלקות TTFB נמוך יותר עקב קריאות עצלות

איך wagmi/viem פותרים את הבעיות של ethers.js

ה-hooks של Wagmi נרשמים אוטומטית לאירועי ארנק (chainChanged, accountsChanged). זה מבטל בעיות של נתונים מיושנים. הקליינטים של Viem הם בלתי משתנים — כל קריאה מחזירה תוצאה חדשה. יחד הם מספקים ראקטיביות ללא דליפות זיכרון. לדוגמה, useWatchContractEvent מאחזר נתונים אוטומטית בעת החלפת רשת, ו-useReadErc20BalanceOf מסנן אירועים מבלי ליצור הרשמות חדשות בכל רינדור מחדש. אנו משתמשים בגישה זו בפרויקטים בייצור, ומפחיתים את עומס ה-RPC ומשפרים את חוויית המשתמש. כפי שצוין ב-התיעוד הרשמי של viem, "viem נועד להיות מודולרי, ניתן ל-tree-shaking, ובטוח בטיפוסים כברירת מחדל".

מה מביאה יצירת טיפוסי ABI

ללא יצירת טיפוסים, כל קריאה לחוזה דורשת ציון ידני של ה-ABI והמרת תוצאות. Wagmi CLI יוצר טיפוסים מקבצי ה-ABI שלכם בפקודה אחת. לאחר היצירה, מקבלים hooks עם טיפוסים מלאים לארגומנטים ולערכי החזרה. זה מבטל שגיאות בכתובות, בארגומנטים, ומאפשר לעורך להציע ערכים אפשריים. בפשטות, מייבאים את npm i -D @wagmi/cli npx wagmi generate ומעבירים את הכתובת הנדרשת — הכל נבדק בזמן הקומפילציה.

npm i -D @wagmi/cli npx wagmi generate 

איך אנו מיישמים wagmi/viem: שלב אחר שלב

הגדרת תצורה

// lib/wagmi.ts
import { createConfig, http } from 'wagmi';
import { mainnet, arbitrum, base } from 'wagmi/chains';
import { injected, walletConnect } from 'wagmi/connectors';

export const config = createConfig({
  chains: [mainnet, arbitrum, base],
  connectors: [
    injected(),
    walletConnect({
      projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!,
    }),
  ],
  transports: {
    [mainnet.id]: http(process.env.ETH_RPC_URL!),
    [arbitrum.id]: http(process.env.ARBITRUM_RPC_URL!),
    [base.id]: http(process.env.BASE_RPC_URL!),
  },
});

קריאת נתונים מחוזה

// hooks/useTokenData.ts
import { useReadContracts } from 'wagmi';
import { erc20Abi, formatUnits } from 'viem';

export function useTokenData(
  tokenAddress: `0x${string}`,
  userAddress?: `0x${string}`
) {
  const { data, isLoading } = useReadContracts({
    contracts: [
      {
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'name',
      },
      {
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'symbol',
      },
      {
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'decimals',
      },
      {
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'totalSupply',
      },
      ...(userAddress
        ? [
            {
              address: tokenAddress,
              abi: erc20Abi,
              functionName: 'balanceOf' as const,
              args: [userAddress] as [`0x${string}`],
            },
          ]
        : []),
    ],
    query: {
      refetchInterval: 30_000,
      staleTime: 10_000,
    },
  });

  const decimals = (data?.[2].result as number) ?? 18;

  return {
    isLoading,
    name: data?.[0].result as string | undefined,
    symbol: data?.[1].result as string | undefined,
    decimals,
    totalSupply: data?.[3].result
      ? formatUnits(data[3].result as bigint, decimals)
      : undefined,
    userBalance:
      userAddress && data?.[4]?.result
        ? formatUnits(data[4].result as bigint, decimals)
        : undefined,
  };
}

כתיבה לחוזה עם סימולציה

// hooks/useTokenTransfer.ts
import { useWriteContract, useWaitForTransactionReceipt, useSimulateContract } from 'wagmi';
import { erc20Abi, parseUnits } from 'viem';
import { useState } from 'react';

export function useTokenTransfer(tokenAddress: `0x${string}`, decimals: number) {
  const [recipient, setRecipient] = useState<`0x${string}` | undefined>();
  const [amount, setAmount] = useState('');
  const amountWei = amount ? parseUnits(amount, decimals) : 0n;

  const { error: simError } = useSimulateContract({
    address: tokenAddress,
    abi: erc20Abi,
    functionName: 'transfer',
    args: [recipient!, amountWei],
    query: { enabled: !!recipient && amountWei > 0n },
  });

  const { writeContract, data: txHash, isPending } = useWriteContract();
  const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash: txHash });

  const transfer = () => {
    if (!recipient || amountWei === 0n) return;
    writeContract({
      address: tokenAddress,
      abi: erc20Abi,
      functionName: 'transfer',
      args: [recipient, amountWei],
    });
  };

  return {
    recipient,
    setRecipient,
    amount,
    setAmount,
    simError,
    transfer,
    txHash,
    isPending,
    isConfirming,
    isSuccess,
  };
}

קליינט viem בצד השרת

// lib/publicClient.ts
import { createPublicClient, http, createWalletClient } from 'viem';
import { mainnet } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';

export const publicClient = createPublicClient({
  chain: mainnet,
  transport: http(process.env.ETH_RPC_URL!),
});

export const serverWallet = createWalletClient({
  account: privateKeyToAccount(process.env.RELAYER_PRIVATE_KEY as `0x${string}`),
  chain: mainnet,
  transport: http(process.env.ETH_RPC_URL!),
});

יצירת טיפוסים מ-ABI

npm i -D @wagmi/cli npx wagmi generate 

לאחר היצירה, מופיעים hooks כמו // lib/wagmi.ts import { createConfig, http } from 'wagmi'; import { mainnet, arbitrum, base } from 'wagmi/chains'; import { injected, walletConnect } from 'wagmi/connectors'; export const config = createConfig({ chains: [mainnet, arbitrum, base], connectors: [ injected(), walletConnect({ projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID! }), ], transports: { [mainnet.id]: http(process.env.ETH_RPC_URL!), [arbitrum.id]: http(process.env.ARBITRUM_RPC_URL!), [base.id]: http(process.env.BASE_RPC_URL!), }, }); עם טיפוסי ארגומנטים מלאים.

דוגמת שימוש ב-Next.js App Router

ברכיבי שרת, השתמשו ב-// hooks/useTokenData.ts import { useReadContracts } from 'wagmi'; import { erc20Abi, formatUnits } from 'viem'; export function useTokenData(tokenAddress: `0x${string}`, userAddress?: `0x${string}`) { const { data, isLoading } = useReadContracts({ contracts: [ { address: tokenAddress, abi: erc20Abi, functionName: 'name' }, { address: tokenAddress, abi: erc20Abi, functionName: 'symbol' }, { address: tokenAddress, abi: erc20Abi, functionName: 'decimals' }, { address: tokenAddress, abi: erc20Abi, functionName: 'totalSupply' }, ...(userAddress ? [{ address: tokenAddress, abi: erc20Abi, functionName: 'balanceOf' as const, args: [userAddress] as [`0x${string}`], }] : []), ], query: { refetchInterval: 30_000, staleTime: 10_000, }, }); const decimals = (data?.[2].result as number) ?? 18; return { isLoading, name: data?.[0].result as string | undefined, symbol: data?.[1].result as string | undefined, decimals, totalSupply: data?.[3].result ? formatUnits(data[3].result as bigint, decimals) : undefined, userBalance: userAddress && data?.[4]?.result ? formatUnits(data[4].result as bigint, decimals) : undefined, }; } ישירות מכיוון ש-// hooks/useTokenTransfer.ts import { useWriteContract, useWaitForTransactionReceipt, useSimulateContract } from 'wagmi'; import { erc20Abi, parseUnits } from 'viem'; import { useState } from 'react'; export function useTokenTransfer(tokenAddress: `0x${string}`, decimals: number) { const [recipient, setRecipient] = useState<`0x${string}` | undefined>(); const [amount, setAmount] = useState(''); const amountWei = amount ? parseUnits(amount, decimals) : 0n; const { error: simError } = useSimulateContract({ address: tokenAddress, abi: erc20Abi, functionName: 'transfer', args: [recipient!, amountWei], query: { enabled: !!recipient && amountWei > 0n }, }); const { writeContract, data: txHash, isPending } = useWriteContract(); const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash: txHash }); const transfer = () => { if (!recipient || amountWei === 0n) return; writeContract({ address: tokenAddress, abi: erc20Abi, functionName: 'transfer', args: [recipient, amountWei], }); }; return { recipient, setRecipient, amount, setAmount, simError, transfer, txHash, isPending, isConfirming, isSuccess, }; } עובד רק בצד הלקוח. צרו // lib/publicClient.ts import { createPublicClient, http, createWalletClient } from 'viem'; import { mainnet } from 'viem/chains'; import { privateKeyToAccount } from 'viem/accounts'; export const publicClient = createPublicClient({ chain: mainnet, transport: http(process.env.ETH_RPC_URL!), }); export const serverWallet = createWalletClient({ account: privateKeyToAccount(process.env.RELAYER_PRIVATE_KEY as `0x${string}`), chain: mainnet, transport: http(process.env.ETH_RPC_URL!), }); והשתמשו בו לקריאות נתונים. לדוגמה, ב-npm i -D @wagmi/cli npx wagmi generate , ייבאו את useReadErc20BalanceOf(...) מ-viem וקראו למתודות ללא hooks של React.

תהליך העבודה שלנו

  • ביקורת — ניתוח היישום הנוכחי, זיהוי בעיות (דליפות זיכרון, נתונים מיושנים).
  • עיצוב — עיצוב שכבת האינטראקציה: תצורות, hooks, קליינטים לשרת.
  • יישום — כתיבת קוד עם יצירת טיפוסים, כיסוי מקרי קצה.
  • בדיקות — סימולציות, בדיקות רשת, בדיקות ידניות עם ארנקים שונים.
  • פריסה — פריסה, ניטור, תיעוד.

מה כלול

  • קוד אינטגרציה עם wagmi/viem: hooks עם טיפוסים.
  • תיעוד לשימוש ולהמשך פיתוח.
  • הגדרת CI/CD לעדכוני ABI אוטומטיים.
  • הדרכת צוות (1–2 מפגשים).
  • אחריות תאימות עם MetaMask, WalletConnect, Coinbase Wallet.

לוחות זמנים

אינטגרציה בסיסית (קריאה/כתיבה עבור 1–2 חוזים) — 1–2 ימים. שכבה מלאה עם יצירת טיפוסים, קליינטים לשרת והרשמות לאירועים — 2–3 ימים. לוחות הזמנים מותאמים לאחר ביקורת בפרויקט שלכם.

הזמינו ביקורת על האינטגרציה שלכם — נמצא צווארי בקבוק תוך יום אחד

השוואת גישות: ethers.js לעומת wagmi/viem

פרמטר ethers.js wagmi/viem
קוד תבניתי גבוה (ניהול ספק ידני) נמוך (תצורה אוטומטית)
עדכון נתונים ידני באמצעות useEffect אוטומטי באמצעות watch
בטיחות טיפוסים לא מלאה (באמצעות @wagmi/cli)
ביצועים ~2 אלפיות שנייה לקריאה <1 אלפית שנייה לקריאה (הערכה עצלה)
תמיכה בצד השרת מורכבת (דורש ethers.providers) פשוטה (viem createPublicClient)

צרו קשר — נדון בפרטי הפרויקט שלכם ונבחר את הארכיטקטורה האופטימלית.