פיתוח טוקני Jetton על TON: ארכיטקטורה, חוזים ופריסה

העברת טוקנים מ-EVM ל-TON משבשת לעיתים קרובות דפוסים מוכרים בשל הארכיטקטורה האסינכרונית, שבה יתרות נמצאות בארנקים נפרדים. אנחנו מפתחים טוקני Jetton לפי תקן TEP-74, מיישמים חוזים חכמים ב-FunC וב-Tact עם תשומת לב לכל הניואנסים האסינכרוניים. הצוות שלנו מטפל בכל המחזור—מעיצוב הארכיטקטורה ועד לפריסה ותמיכה שוטפת—ומספק פתרון אמין וסקלבילי.

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

שאלות נפוצות

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

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1482
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1336
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1035
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1294
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    739
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1033

פיתוח טוקני Jetton על TON: ארכיטקטורה, חוזים ופריסה

כאשר מעבירים טוקנים מ-EVM ל-TON, מפתחים רבים מופתעים מהאסינכרוניות: היתרה של מחזיק אינה נמצאת בחוזה אחד אלא בארנק Jetton נפרד. זה שובר דפוסים מוכרים — העברה הופכת לשרשרת של הודעות, לא לקריאה אטומית. לצוות שלנו יש ניסיון של למעלה מ-5 שנים בפיתוח בלוקצ'יין ואנו מבטיחים יישום נכון של תקן Jetton במפתח פתוח. השלמנו בהצלחה למעלה מ-10 פרויקטים על TON, כולל פרוטוקולי DeFi ושווקי NFT. חיסכון באופטימיזציית גז יכול להגיע ל-30% בשימוש ב-Tact, מה שמפחית את עלויות הבעלות על הטוקן. צרו קשר לפיתוח טוקן Jetton במפתח פתוח — אנו מבטיחים אבטחה ואופטימיזציית גז.

ארכיטקטורה: שני חוזים

Jetton Master — החוזה המרכזי, מאחסן מטא-דאטה של הטוקן (שם, סימבול, עשרוניות, total_supply) ויכול להנפיק ארנקי Jetton חדשים. המפרט המלא מתואר ב-TEP-74.

Jetton Wallet — מופע אחד לכל מחזיק. מאחסן את היתרה של כתובת ספציפית. בעת העברה, ארנק ה-Jetton של השולח שולח הודעה לארנק ה-Jetton של המקבל. זו אינה קריאה אטומית אלא שרשרת של הודעות אסינכרוניות. אם ארנק ה-Jetton של המקבל אינו קיים עדיין, הוא נוצר בקבלת הטוקן הראשונה, והשולח משלם עבור הפריסה (בערך 0.04 TON כפיקדון אחסון). אחסון על TON כרוך בעמלות אחסון — כ-0.005 TON לקילובייט בחודש, ולכן כל ארנק חייב לשמור על יתרת TON חיובית. בפועל, אנו מוסיפים מינימום של 0.05 TON לכל ארנק — כ-0.1 דולר בשערים הנוכחיים — כדי למנוע הקפאה.

כיצד פועל חוזה Jetton?

תקן TEP-74 מגדיר מבני הודעות ולוגיקת העברה. בואו נסתכל על עיבוד העברה בארנק Jetton:

;; Jetton Wallet: обработка transfer сообщения
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
    if (op == op::transfer()) {
        int query_id = in_msg_body~load_uint(64);
        int jetton_amount = in_msg_body~load_coins();
        slice to_owner_address = in_msg_body~load_msg_addr();
        slice response_address = in_msg_body~load_msg_addr();
        cell custom_payload = in_msg_body~load_maybe_ref();
        int forward_ton_amount = in_msg_body~load_coins();
        slice forward_payload = in_msg_body;
        throw_unless(error::not_enough_jettons, jetton_amount <= balance);
        balance -= jetton_amount;
        save_data();
        var msg_body = begin_cell()
            .store_uint(op::internal_transfer(), 32)
            .store_uint(query_id, 64)
            .store_coins(jetton_amount)
            .store_slice(my_address())
            .store_slice(response_address)
            .store_coins(forward_ton_amount)
            .store_slice(forward_payload)
            .end_cell();
        var to_wallet_address = calc_jetton_wallet_address(to_owner_address);
        send_raw_message(
            begin_cell()
                .store_uint(0x18, 6)
                .store_slice(to_wallet_address)
                .store_coins(forward_ton_amount + min_ton_for_storage)
                .store_uint(1, 107)
                .store_ref(msg_body)
                .end_cell(),
            64
        );
    }
}

הקוד לעיל הוא הבסיס של ארנק Jetton סטנדרטי. שימו לב לקריאה ל-;; Jetton Wallet: обработка transfer сообщения () recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure { if (op == op::transfer()) { int query_id = in_msg_body~load_uint(64); int jetton_amount = in_msg_body~load_coins(); slice to_owner_address = in_msg_body~load_msg_addr(); slice response_address = in_msg_body~load_msg_addr(); cell custom_payload = in_msg_body~load_maybe_ref(); int forward_ton_amount = in_msg_body~load_coins(); slice forward_payload = in_msg_body; throw_unless(error::not_enough_jettons, jetton_amount <= balance); balance -= jetton_amount; save_data(); var msg_body = begin_cell() .store_uint(op::internal_transfer(), 32) .store_uint(query_id, 64) .store_coins(jetton_amount) .store_slice(my_address()) .store_slice(response_address) .store_coins(forward_ton_amount) .store_slice(forward_payload) .end_cell(); var to_wallet_address = calc_jetton_wallet_address(to_owner_address); send_raw_message( begin_cell() .store_uint(0x18, 6) .store_slice(to_wallet_address) .store_coins(forward_ton_amount + min_ton_for_storage) .store_uint(1, 107) .store_ref(msg_body) .end_cell(), 64 ); } } — כתובת המקבל מחושבת דטרמיניסטית מכתובת המחזיק וקוד הארנק.

למה לבחור ב-Tact על פני FunC?

Tact היא שפה ברמה גבוהה שמהדרת ל-FunC. התחביר שלה דומה ל-TypeScript, מה שמאיץ משמעותית את הפיתוח. Tact מהירה פי 5 ליצירת טוקני Jetton בהשוואה ל-FunC, במיוחד עבור צוותים עם ניסיון ב-EVM. דוגמת חוזה ב-Tact:

import "@stdlib/deploy";
import "@stdlib/jetton";

contract JettonMaster with Deployable, Jetton {
    totalSupply: Int as coins;
    owner: Address;
    content: Cell;
    mintable: Bool;

    init(owner: Address, content: Cell) {
        self.totalSupply = 0;
        self.owner = owner;
        self.content = content;
        self.mintable = true;
    }

    receive(msg: TokenMint) {
        require(sender() == self.owner, "Not owner");
        require(self.mintable, "Not mintable");
        self.totalSupply += msg.amount;
        let winit: StateInit = self.getJettonWalletInit(msg.receiver);
        let walletAddress: Address = contractAddress(winit);
        send(SendParameters{
            to: walletAddress,
            value: ton("0.05"),
            mode: SendIgnoreErrors,
            bounce: false,
            body: TokenTransferInternal{
                queryId: 0,
                amount: msg.amount,
                from: myAddress(),
                responseAddress: msg.receiver,
                forwardTonAmount: 0,
                forwardPayload: emptySlice(),
            }.toCell(),
            code: winit.code,
            data: winit.data,
        });
    }
}
מאפיין FunC Tact
רמת הפשטה נמוכה גבוהה
תחביר ייחודי דומה ל-TypeScript
מהירות פיתוח איטית גבוהה (פי 5 מהר יותר)
שליטה מלאה חלקית (באמצעות הוספות FunC)

כיצד אנו מבטיחים אבטחת טוקני Jetton

Reentrancy הוא איום מרכזי בסביבה האסינכרונית של TON. בניגוד ל-EVM, שבו המצב נעול עד סוף העסקה, ב-TON כל קריאה היא הודעה נפרדת. אם מטפל ההעברה אינו בודק יתרה לפני ואחרי הפעולות, תוקף יכול ליזום קריאה חוזרת לפני שינוי המצב. בפרויקטים שלנו, אנו משתמשים בתבנית "check-effects-interactions" ומוסיפים הגנה מפני reentrancy באמצעות דגלים בנתוני הארנק. אנו גם בודקים ביסודיות את כל התרחישים באמצעות fuzzing: Echidna עבור FunC ו-fuzzers מובנים ב-Blueprint.

תהליך: שלבים

  1. ניתוח: דיון בדרישות הטוקן (סטנדרטי או מותאם), בחירת מחסנית (Tact/FunC).
  2. עיצוב: ארכיטקטורת חוזים, הגדרת מכניקות (מס העברה, רשימה לבנה, vesting).
  3. יישום: כתיבת Jetton Master ו-Jetton Wallet, בדיקות יחידה באמצעות Blueprint sandbox.
  4. בדיקות: סימולציה של כל התרחישים, בדיקת reentrancy ואופטימיזציית גז.
  5. פריסה ואימות: פריסה ל-mainnet, אימות קוד ב-tonviewer.com, אינטגרציה עם ארנקים.
שלב משך תוצאה
ניתוח 1–2 ימים מפרט טכני
עיצוב 1–2 ימים ארכיטקטורת חוזים
יישום 3–7 ימים קוד מקור + בדיקות
בדיקות 1–2 ימים דוח בדיקות
פריסה + אימות 1–2 ימים טוקן פעיל על mainnet

גז ואחסון: מאפייני TON

ב-TON, גז פועל אחרת מאשר ב-EVM. הבדלים מרכזיים:

  • עמלת אחסון — חשבונות משלמים שכירות עבור אחסון נתונים. אם יתרת ה-TON בארנק Jetton יורדת לאפס, החשבון מוקפא והנתונים אובדים. הפיקדון המינימלי המומלץ לארנק Jetton הוא 0.05 TON.
  • העברת TON — בעת שליחת Jetton עם calc_jetton_wallet_address, חוזה המקבל מקבל הודעה עם TON מצורף. זה מקביל ל-import "@stdlib/deploy"; import "@stdlib/jetton"; contract JettonMaster with Deployable, Jetton { totalSupply: Int as coins; owner: Address; content: Cell; mintable: Bool; init(owner: Address, content: Cell) { self.totalSupply = 0; self.owner = owner; self.content = content; self.mintable = true; } receive(msg: TokenMint) { require(sender() == self.owner, "Not owner"); require(self.mintable, "Not mintable"); self.totalSupply += msg.amount; let winit: StateInit = self.getJettonWalletInit(msg.receiver); let walletAddress: Address = contractAddress(winit); send(SendParameters{ to: walletAddress, value: ton("0.05"), mode: SendIgnoreErrors, bounce: false, body: TokenTransferInternal{ queryId: 0, amount: msg.amount, from: myAddress(), responseAddress: msg.receiver, forwardTonAmount: 0, forwardPayload: emptySlice(), }.toCell(), code: winit.code, data: winit.data, }); } } , אבל בסגנון TON.
  • גז לעסקת העברה הוא כ-0.001 TON, זול משמעותית מ-Ethereum במחירים הנוכחיים.

בדיקות

מסגרת ה-Sandbox (Blueprint) הרשמית מאפשרת בדיקת חוזים ב-TypeScript. דוגמת בדיקה:

import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
import { JettonMaster } from '../wrappers/JettonMaster';
import { JettonWallet } from '../wrappers/JettonWallet';

describe('Jetton', () => {
  let blockchain: Blockchain;
  let deployer: SandboxContract<TreasuryContract>;
  let jettonMaster: SandboxContract<JettonMaster>;

  beforeEach(async () => {
    blockchain = await Blockchain.create();
    deployer = await blockchain.treasury('deployer');
    jettonMaster = blockchain.openContract(
      await JettonMaster.fromInit(deployer.address, buildMetadataCell())
    );
    await jettonMaster.send(
      deployer.getSender(),
      { value: toNano('0.1') },
      { $$type: 'Deploy', queryId: 0n }
    );
  });

  it('should mint tokens', async () => {
    const receiver = await blockchain.treasury('receiver');
    const mintResult = await jettonMaster.send(
      deployer.getSender(),
      { value: toNano('0.2') },
      { $$type: 'TokenMint', queryId: 0n, amount: toNano('1000'), receiver: receiver.address }
    );
    expect(mintResult.transactions).toHaveTransaction({
      from: jettonMaster.address,
      deploy: true,
      success: true,
    });
    const walletAddress = await jettonMaster.getGetWalletAddress(receiver.address);
    const wallet = blockchain.openContract(JettonWallet.fromAddress(walletAddress));
    const data = await wallet.getGetWalletData();
    expect(data.balance).toBe(toNano('1000'));
  });
});

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

פיתוח Jetton Master ו-Jetton Wallet ב-Tact (או FunC לפי בקשה), בדיקות באמצעות Blueprint sandbox, פריסה ל-mainnet של TON, אימות דרך tonviewer.com, וסקריפטים עטיפה ב-TypeScript לאינטגרציה. לוחות זמנים: 5–10 ימים ל-Jetton סטנדרטי, 2–4 שבועות למכניקות מותאמות אישית. חיסכון בעלויות פיתוח בשימוש ב-Tact יכול להגיע ל-30% הודות ליישום מהיר יותר.

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