פיתוח מערכת ניהול אישורי טוקנים (Revoke)

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

שירותי פיתוח בלוקצ'יין

שאלות נפוצות

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

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1481
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1335
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1034
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1293
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    738
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1031

פיתוח מערכת ניהול אישורי טוקנים (ביטול הרשאות)

נתקלנו במצב: למשתמש יש מאות אישורי טוקנים שאינם בשימוש, אחד הפרוטוקולים נפרץ — והארנק מתרוקן. ללא מערכת ניהול אישורים, כל פרוטוקול שנפרץ יכול לרוקן את הכספים שלך. הצוות שלנו מפתח פתרונות מותאמים אישית לצפייה וביטול אישורי טוקנים — מממשק חד-שרשרתי פשוט ועד מערכת רב-שרשרתית עם דירוג סיכונים וביטול בכמות גדולה. פיתוח כזה אורך בין 2 ל-5 ימים בהתאם למורכבות, ואנחנו עושים זאת במפתח מלא: מניתוח נתונים ועד פריסה. צור קשר להערכת פרויקט.

יסודות טכניים: כיצד פועלים אישורים

ERC-20 Allowance

תקן ERC-20 מגדיר allowance(owner, spender) — כמה טוקנים המוציא (spender) יכול לבזבז בשם הבעלים. הוא מוגדר באמצעות approve(spender, amount). ערך של type(uint256).max (2^256-1) פירושו "בלתי מוגבל" — רוב הפרוטוקולים דורשים זאת לנוחות.

בעיה: לאישור אין תפוגה. אין מנגנון ביטול אוטומטי. אם פרוטוקול נפרץ שנה אחרי האישור שלך — האישור עדיין פעיל.

אישורי ERC-721 ו-ERC-1155

עבור NFT, ישנם שני סוגי אישורים:

  • approve(operator, tokenId) — הרשאה לטוקן ספציפי
  • setApprovalForAll(operator, true) — גישה מלאה לכל האוסף

setApprovalForAll משמש את OpenSea, blur.io ופלטפורמות מסחר אחרות. זהו הסוג המסוכן ביותר — שוק אחד שנפרץ עם setApprovalForAll פעיל שווה לאוסף אבוד.

EIP-2612: Permit

permit(owner, spender, value, deadline, v, r, s) — חתימה במקום עסקה. הוא לא יוצר אישור קבוע, פועל פעם אחת עם מועד אחרון מוגדר. dApps מתוכננים היטב משתמשים ב-permit במקום approve.

אבל ל-permit יש ניואנס: אם DAI, USDC או טוקן אחר תומך ב-permit — האישור דרך permit עדיין ניתן לצפייה באמצעות allowance(). הם בלתי ניתנים להבחנה מ-approve רגיל.

קריאת נתוני אישורים

דרך אירוע Approval

קריאה ישירה ל-allowance(owner, spender) דורשת ידיעת כתובת המוציא. כדי לקבל את כל האישורים הפעילים עבור ארנק — יש לקרוא אירועים:

import { createPublicClient, http, parseAbi } from 'viem';

const ERC20_ABI = parseAbi([
  'event Approval(address indexed owner, address indexed spender, uint256 value)',
  'function allowance(address owner, address spender) view returns (uint256)',
  'function symbol() view returns (string)',
  'function decimals() view returns (uint8)',
]);

async function getTokenApprovals(ownerAddress: `0x${string}`) {
  const client = createPublicClient({
    chain: mainnet,
    transport: http(RPC_URL)
  });

  const approvalLogs = await client.getLogs({
    event: ERC20_ABI[0],
    args: { owner: ownerAddress },
    fromBlock: 0n,
    toBlock: 'latest'
  });

  const latestApprovals = new Map<string, typeof approvalLogs[0]>();
  for (const log of approvalLogs) {
    const key = `${log.address}-${log.args.spender}`;
    latestApprovals.set(key, log);
  }

  const results = await Promise.all(
    Array.from(latestApprovals.values()).map(async (log) => {
      const [allowance, symbol, decimals] = await Promise.all([
        client.readContract({
          address: log.address,
          abi: ERC20_ABI,
          functionName: 'allowance',
          args: [ownerAddress, log.args.spender!]
        }),
        client.readContract({
          address: log.address,
          abi: ERC20_ABI,
          functionName: 'symbol'
        }),
        client.readContract({
          address: log.address,
          abi: ERC20_ABI,
          functionName: 'decimals'
        }),
      ]);

      return {
        tokenAddress: log.address,
        spenderAddress: log.args.spender!,
        allowance,
        symbol,
        decimals,
        isUnlimited: allowance === BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'),
      };
    })
  );

  return results.filter(r => r.allowance > 0n);
}

בעיה עם נתונים היסטוריים

import { createPublicClient, http, parseAbi } from 'viem'; const ERC20_ABI = parseAbi([ 'event Approval(address indexed owner, address indexed spender, uint256 value)', 'function allowance(address owner, address spender) view returns (uint256)', 'function symbol() view returns (string)', 'function decimals() view returns (uint8)', ]); async function getTokenApprovals(ownerAddress: `0x${string}`) { const client = createPublicClient({ chain: mainnet, transport: http(RPC_URL) }); const approvalLogs = await client.getLogs({ event: ERC20_ABI[0], args: { owner: ownerAddress }, fromBlock: 0n, toBlock: 'latest' }); const latestApprovals = new Map<string, typeof approvalLogs[0]>(); for (const log of approvalLogs) { const key = `${log.address}-${log.args.spender}`; latestApprovals.set(key, log); } const results = await Promise.all( Array.from(latestApprovals.values()).map(async (log) => { const [allowance, symbol, decimals] = await Promise.all([ client.readContract({ address: log.address, abi: ERC20_ABI, functionName: 'allowance', args: [ownerAddress, log.args.spender!] }), client.readContract({ address: log.address, abi: ERC20_ABI, functionName: 'symbol' }), client.readContract({ address: log.address, abi: ERC20_ABI, functionName: 'decimals' }), ]); return { tokenAddress: log.address, spenderAddress: log.args.spender!, allowance, symbol, decimals, isUnlimited: allowance === BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'), }; }) ); return results.filter(r => r.allowance > 0n); } עם getLogs איטי ויקר עבור RPC ציבוריים. פתרונות:

  • The Graph: אינדוקס של אירועי Approval דרך subgraph, שאילתות GraphQL מיידיות
  • Etherscan/Alchemy API: נקודות קצה מוכנות לאישורי טוקנים (fromBlock: 0n)
  • אינדוקס מצטבר: מעקב אחר הבלוק האחרון שמונפק, בקשת אירועים חדשים בלבד בכל עדכון

עבור מערכות ייצור, subgraph של The Graph הוא הפתרון האופטימלי. שאילתה אחת מחזירה את כל האישורים הפעילים עם מטא-דאטה.

מדוע ביטול בכמות גדולה הוא משימה מורכבת?

ביטול 10 אישורים דרך ERC-20 דורש 10 עסקאות נפרדות, כל אחת עם חתימת המשתמש. זה בלתי מקובל מבחינת חוויית משתמש. Multicall לא עוזר כי alchemy_getTokenAllowances הוא פעולה בשם המשתמש, הדורשת חתימה. פתרון — תור עסקאות עם המשך אוטומטי לאחר אישור. המשתמש חותם על כל אחת אבל רואה התקדמות "מבטל 3 מתוך 8…". לחלופין, שימוש ב-Permit2 לביטול בכמות גדולה, אם המשתמש העביר את האישורים שלו ל-Permit2 — זה מפחית עלויות גז בעד 70%.

כיצד מתבצע ביטול טוקנים?

ביטול ERC-20

ביטול = approve. עסקה אחת לכל זוג טוקן+מוציא.

async function revokeERC20Approval(
  tokenAddress: `0x${string}`,
  spenderAddress: `0x${string}`
) {
  const { writeContract } = useWriteContract();
  writeContract({
    address: tokenAddress,
    abi: erc20Abi,
    functionName: 'approve',
    args: [spenderAddress, 0n]
  });
}

ביטול ERC-721 / ERC-1155

approve(spender, 0) — ביטול גישה מלאה לאוסף. קריטי יותר, ולכן מודגש באדום בממשק. async function revokeERC20Approval( tokenAddress: `0x${string}`, spenderAddress: `0x${string}` ) { const { writeContract } = useWriteContract(); writeContract({ address: tokenAddress, abi: erc20Abi, functionName: 'approve', args: [spenderAddress, 0n] }); } ואחריו ביטול פחות קריטי — גישה לטוקן ספציפי.

עיצוב ממשק המשתמש של המערכת

הרכיב המרכזי הוא טבלה עם מיון וסינון:

טוקן מוציא אישור סיכון פעולה
USDC Uniswap V3 בלתי מוגבל בינוני בטל
WETH פרוטוקול ישן (לא פעיל) בלתי מוגבל גבוה בטל
DAI Aave V3 1,000 DAI נמוך בטל

דירוג סיכונים הוא תכונת UX חשובה. כתובות מוציא מזוהות באמצעות:

  • Etherscan Labels API
  • מאגר פרוטוקולים של DefiLlama
  • רשימה לבנה מותאמת אישית של פרוטוקולים ידועים

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

סינונים: לפי רשת, לפי סוג (ERC-20 / NFT), לפי רמת סיכון, רק אישורים בלתי מוגבלים.

השוואת גישות לאחזור נתונים

גישה מהירות מורכבות גז עבור שאילתות
אירועי RPC איטית נמוכה גבוה (קריאות רבות)
The Graph פי 10 מהיר יותר בינונית (נדרש subgraph) אפס (GraphQL)
Alchemy API מהירה נמוכה (נקודת קצה מוכנה) מבוסס מנוי

מה כלול בעבודה שלנו

  • פיתוח ממשק משתמש (React + wagmi + TanStack Table) עם טבלת אישורים
  • אינטגרציה עם מקור נתונים נבחר (RPC, The Graph, Alchemy)
  • יישום ביטול בכמות גדולה דרך תור עסקאות
  • תמיכה רב-שרשרתית (Ethereum, Arbitrum, Polygon, Base, BNB Chain)
  • דירוג סיכונים מבוסס רשימה לבנה ו-API חיצוניים
  • תיעוד API והוראות משתמש
  • פריסה על הדומיין שלך או white-label

החברה במספרים

מעל 5 שנות ניסיון בפיתוח בלוקצ'יין, למעלה מ-30 פרויקטים מיושמים, כולל פרוטוקולי DeFi ופלטפורמות NFT. הצוות שלנו מורכב ממהנדסים בכירים עם ידע עמוק ב-Solidity, Rust (Solana) ו-TypeScript.

לוחות זמנים משוערים

מערכת בסיסית לרשת אחת (ERC-20, אירועי RPC) — מיומיים. רב-שרשרתית עם NFT ו-The Graph — מ-5 ימים. העלות המדויקת מחושבת באופן אישי — צור קשר לייעוץ.