פיתוח טוקן דפלציוני: שריפה, ביקורת, אינטגרציית DeFi

מודלים של טוקנים דפלציוניים מובילים לעיתים קרובות לעימותים עם AMM ולשגיאות במהלך שריפה. אנחנו בונים טוקנים דפלציוניים במפתח מלא—מארכיטקטורת fee-on-transfer ו-buyback ועד ביקורות ושילוב DeFi. הצוות שלנו מטפל במחזור המלא: תכנון, יישום, הגדרת חריגים ותמיכה מתמשכת, ומבטיח שהמנגנונים פועלים באמינות וללא תקלות.

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

שאלות נפוצות

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

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

פיתוח טוקן דפלציוני (עם שריפה)

פרויקטים רבים בוחרים במנגנון הדפלציוני אך נתקלים בבעיות לא מובנות מאליהן: עמלת העברה (fee-on-transfer) מתנגשת עם AMM, ו-buyback דורש חוזה ניטור נפרד. אנו מפתחים טוקנים כאלה במפתח מלא — מאז השקת תחום ה-DeFi סיפקנו מעל 10 פרויקטים, שעברו ביקורות (audits) על ידי שתי צוותים מהשורה הראשונה. שימוש ב-timelocks מפחית את עלויות הגז בעד 30% בהשוואה לניטור ידני — עם נפח עסקאות ממוצע של 10,000 בחודש, החיסכון מסתכם בכ-$500.

לאחרונה פנה אלינו פרויקט עם טוקן שנסחר ב-Uniswap V3. לאחר כל העברה, 2% נשרפו, אך הבריכה זרקה שגיאות INSUFFICIENT_INPUT_AMOUNT ללא הרף. הבעיה התבררה כקריאת SupportingFeeOnTransferTokens הסטנדרטית — היא לא מתחשבת במס. שכתבנו את האינטגרציה לשימוש ב-isBurnExempt והגדרנו רשימת פטורים לבריכה. הסוחרים הפסיקו לאבד כספים. צרו קשר לייעוץ על הטוקונומיקה שלכם — נפרק מקרים דומים.

מדוע fee-on-transfer מתנגשת עם AMM?

תקן ERC-20 אינו מיועד למסי העברה. Uniswap V2 ו-PancakeSwap (במיוחד wrappers) לא מתחשבים בעמלה — מה שגורם לשגיאות transfer. הפתרון הוא להשתמש ב-// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable2Step.sol"; contract DeflationaryToken is ERC20, Ownable2Step { uint256 public burnBps; // базисные пункты, 100 = 1% uint256 public constant MAX_BURN_BPS = 1000; // 10% максимум // Адреса исключённые из налога (LP пары, роутеры) mapping(address => bool) public isBurnExempt; event BurnBpsUpdated(uint256 oldBps, uint256 newBps); constructor( string memory name, string memory symbol, uint256 initialSupply, uint256 _burnBps ) ERC20(name, symbol) Ownable2Step() { require(_burnBps <= MAX_BURN_BPS, "Burn too high"); burnBps = _burnBps; _mint(msg.sender, initialSupply); } function _transfer( address from, address to, uint256 amount ) internal override { if (burnBps > 0 && !isBurnExempt[from] && !isBurnExempt[to]) { uint256 burnAmount = (amount * burnBps) / 10000; uint256 sendAmount = amount - burnAmount; super._transfer(from, address(0), burnAmount); // burn super._transfer(from, to, sendAmount); // transfer } else { super._transfer(from, to, amount); } } function setBurnBps(uint256 _burnBps) external onlyOwner { require(_burnBps <= MAX_BURN_BPS, "Burn too high"); emit BurnBpsUpdated(burnBps, _burnBps); burnBps = _burnBps; } function setBurnExempt(address account, bool exempt) external onlyOwner { isBurnExempt[account] = exempt; } } בצד הלקוח (frontend) ולהגדיר amountIn עבור זוגות טוקנים. אם הבעלים יכול להוסיף כתובות לרשימת הפטורים ללא בקרה, תוקף עם מפתח שנפרץ יכול להשבית את השריפה. אנו מיישמים timelock (48 שעות) על כל שינוי בפרמטרי השריפה.

באיזו גישה לבחור: fee-on-transfer או buyback-and-burn?

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

Fee-on-transfer (שריפה אוטומטית בעת העברה)

כל amountIn - burnAmount שורף אוטומטית X% מהסכום. קוד החוזה:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";

contract DeflationaryToken is ERC20, Ownable2Step {
    uint256 public burnBps; // базисные пункты, 100 = 1%
    uint256 public constant MAX_BURN_BPS = 1000; // 10% максимум

    // Адреса исключённые из налога (LP пары, роутеры)
    mapping(address => bool) public isBurnExempt;

    event BurnBpsUpdated(uint256 oldBps, uint256 newBps);

    constructor(
        string memory name,
        string memory symbol,
        uint256 initialSupply,
        uint256 _burnBps
    ) ERC20(name, symbol) Ownable2Step() {
        require(_burnBps <= MAX_BURN_BPS, "Burn too high");
        burnBps = _burnBps;
        _mint(msg.sender, initialSupply);
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal override {
        if (burnBps > 0 && !isBurnExempt[from] && !isBurnExempt[to]) {
            uint256 burnAmount = (amount * burnBps) / 10000;
            uint256 sendAmount = amount - burnAmount;
            super._transfer(from, address(0), burnAmount); // burn
            super._transfer(from, to, sendAmount); // transfer
        } else {
            super._transfer(from, to, amount);
        }
    }

    function setBurnBps(uint256 _burnBps) external onlyOwner {
        require(_burnBps <= MAX_BURN_BPS, "Burn too high");
        emit BurnBpsUpdated(burnBps, _burnBps);
        burnBps = _burnBps;
    }

    function setBurnExempt(address account, bool exempt) external onlyOwner {
        isBurnExempt[account] = exempt;
    }
}

בעיה קריטית: הנתב (router) של Uniswap V2 שולח swapExactTokensForTokensSupportingFeeOnTransferTokens, אך הבריכה מקבלת IUniswapV2Router02(router).swapExactTokensForTokensSupportingFeeOnTransferTokens( amountIn, amountOutMin, path, to, deadline ); . הפתרון הוא להשתמש ב-contract BuybackBurnVault is Ownable2Step { IERC20 public immutable token; IUniswapV2Router02 public immutable router; uint256 public totalBurned; event BuybackExecuted(uint256 bnbSpent, uint256 tokensBurned); constructor(address _token, address _router) Ownable2Step() { token = IERC20(_token); router = IUniswapV2Router02(_router); } receive() external payable {} function executeBuyback( uint256 bnbAmount, uint256 minTokensOut, uint256 deadline ) external onlyOwner { require(address(this).balance >= bnbAmount, "Insufficient BNB"); address[] memory path = new address[](2); path[0] = router.WETH(); // WBNB на BSC path[1] = address(token); uint256[] memory amounts = router.swapExactETHForTokens{value: bnbAmount}( minTokensOut, path, address(this), deadline ); uint256 tokensBought = amounts[amounts.length - 1]; token.transfer(address(0), tokensBought); totalBurned += tokensBought; emit BuybackExecuted(bnbAmount, tokensBought); } } .

IUniswapV2Router02(router).swapExactTokensForTokensSupportingFeeOnTransferTokens(
    amountIn,
    amountOutMin,
    path,
    to,
    deadline
);

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

שריפה ידנית באמצעות buyback-and-burn

גישה מבוקרת יותר: הפרוטוקול צובר עמלות וקונה מעת לעת טוקנים מהשוק כדי לשרוף. קוד:

contract BuybackBurnVault is Ownable2Step {
    IERC20 public immutable token;
    IUniswapV2Router02 public immutable router;
    uint256 public totalBurned;

    event BuybackExecuted(uint256 bnbSpent, uint256 tokensBurned);

    constructor(address _token, address _router) Ownable2Step() {
        token = IERC20(_token);
        router = IUniswapV2Router02(_router);
    }

    receive() external payable {}

    function executeBuyback(
        uint256 bnbAmount,
        uint256 minTokensOut,
        uint256 deadline
    ) external onlyOwner {
        require(address(this).balance >= bnbAmount, "Insufficient BNB");

        address[] memory path = new address[](2);
        path[0] = router.WETH(); // WBNB on BSC
        path[1] = address(token);

        uint256[] memory amounts = router.swapExactETHForTokens{value: bnbAmount}(
            minTokensOut,
            path,
            address(this),
            deadline
        );

        uint256 tokensBought = amounts[amounts.length - 1];
        token.transfer(address(0), tokensBought);
        totalBurned += tokensBought;

        emit BuybackExecuted(bnbAmount, tokensBought);
    }
}

Buyback-and-burn דורש יותר קוד אך מציע גמישות גדולה פי 3 בהגדרת הטוקונומיקה — ניתן לשנות את התדירות והנפח של ה-buybacks ללא פריסת חוזה חדש.

השוואת גישות

פרמטר Fee-on-transfer Buyback-and-burn
תאימות DeFi מורכבת (דורש SupportFeeOnTransfer) מלאה
מורכבות יישום בינונית גבוהה
שליטה בטוקונומיקה נמוכה (אחוז קבוע) גבוהה (פרמטרים ניתנים להתאמה)
שקיפות גבוהה (אירועים אוטומטיים) בינונית (תלויה בפרסום הביצוע)

איזה אחוז שריפה לבחור?

1–2% הוא אגרסיבי למסחר בתדירות גבוהה. כל החלפה ב-Uniswap = קנייה + מכירה = 2 העברות + עמלת AMM. עם שריפה של 1%, הטוקן מאבד 2% לכל עסקה + 0.3% עמלת LP. זה מרתיע סוחרים. עבור טוקני utility עם העברות נדירות, זה מקובל.

שריפה קבועה לעומת דינמית?

שריפה דינמית (למשל, גבוהה יותר על נפח גדול) מסבכת את הטוקונומיקה אך מאפשרת התאמת לחץ המכירה לתנאי השוק.

מדוע יש צורך במכסת שריפה?

עם שריפה אגרסיבית, ההיצע עלול לרדת לרמות לא נזילות. אנו קובעים סף מינימלי: אם totalSupply < MIN_SUPPLY, השריפה נעצרת.

uint256 public constant MIN_SUPPLY = 1_000_000 * 10**18; // 1M токенов — минимум

function _transfer(address from, address to, uint256 amount) internal override {
    if (burnBps > 0 && !isBurnExempt[from] && !isBurnExempt[to]) {
        uint256 burnAmount = (amount * burnBps) / 10000;
        uint256 currentSupply = totalSupply();
        if (currentSupply > MIN_SUPPLY) {
            if (currentSupply - burnAmount < MIN_SUPPLY) {
                burnAmount = currentSupply - MIN_SUPPLY;
            }
            super._transfer(from, address(0), burnAmount);
            super._transfer(from, to, amount - burnAmount);
            return;
        }
    }
    super._transfer(from, to, amount);
}

כיצד להגן על טוקן דפלציוני מפני התקפות?

שני סיכונים ספציפיים לטוקנים דפלציוניים:

  • Re-entrancy דרך approve. אם uint256 public constant MIN_SUPPLY = 1_000_000 * 10**18; // 1M токенов — минимум function _transfer(address from, address to, uint256 amount) internal override { if (burnBps > 0 && !isBurnExempt[from] && !isBurnExempt[to]) { uint256 burnAmount = (amount * burnBps) / 10000; uint256 currentSupply = totalSupply(); if (currentSupply > MIN_SUPPLY) { if (currentSupply - burnAmount < MIN_SUPPLY) { burnAmount = currentSupply - MIN_SUPPLY; } super._transfer(from, address(0), burnAmount); super._transfer(from, to, amount - burnAmount); return; } } super._transfer(from, to, amount); } מבצע קריאות חיצוניות (למשל, המרת חלק מהעמלה אוטומטית), זו התקפה קלאסית. פתרון: _transfer + תבנית CEI.
  • מניפולציה ברשימת הפטורים. השתמשו ב-timelock על שינויים עבור פרויקטים עם TVL משמעותי.
uint256 public constant BURN_CHANGE_TIMELOCK = 48 hours;
mapping(bytes32 => uint256) public pendingChanges;

function scheduleBurnBpsChange(uint256 newBps) external onlyOwner {
    bytes32 changeId = keccak256(abi.encodePacked("burnBps", newBps));
    pendingChanges[changeId] = block.timestamp + BURN_CHANGE_TIMELOCK;
}

function executeBurnBpsChange(uint256 newBps) external onlyOwner {
    bytes32 changeId = keccak256(abi.encodePacked("burnBps", newBps));
    require(pendingChanges[changeId] != 0, "Not scheduled");
    require(block.timestamp >= pendingChanges[changeId], "Timelock active");
    burnBps = newBps;
    delete pendingChanges[changeId];
}

אנו משתמשים ב-OpenZeppelin Ownable2Step לבקרת גישה.

מהם שלבי הפיתוח?

  1. ייעוץ וניתוח טוקונומיקה (יום אחד).
  2. עיצוב המנגנון (fee-on-transfer או buyback-and-burn).
  3. פיתוח חוזה + כתיבת בדיקות (Foundry/Hardhat) — 5–8 ימים.
  4. בדיקת תאימות עם Uniswap/PancakeSwap — 1–2 ימים.
  5. פריסה, אימות, הגדרת זוג LP — יום אחד.
  6. אופציונלי: הגדרת subgraph לניטור (2–3 ימים).
  7. תיעוד והדרכת צוות.
שלב משך
ניתוח טוקונומיקה יום אחד
פיתוח ובדיקות 5–8 ימים
אינטגרציית AMM 1–2 ימים
פריסה ואימות יום אחד
Subgraph (אופציונלי) 2–3 ימים

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

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

  • ביקורת ואופטימיזציה של טוקונומיקה.
  • פיתוח חוזה חכם (Solidity 0.8.x).
  • בדיקות יחידה ו-fuzz (Foundry).
  • פריסה לרשת הנבחרת (Ethereum, BSC, Polygon).
  • אימות ב-Etherscan/BscScan.
  • הגדרת רשימת פטורים ל-AMM.
  • תיעוד לאינטגרטורים.
  • אחריות על החוזה ל-30 יום.