אנחנו צוות של מהנדסי בלוקצ'יין עם 7 שנות ניסיון ב-Web3. בשלוש השנים האחרונות פרסנו יותר מ-15 חוזים חכמים על ה-parachains של Astar ו-Kusama. כאשר לקוח פונה אלינו כדי להעביר את פרוטוקול ה-DeFi שלו מ-Ethereum ל-Polkadot, הדבר הראשון שאנחנו מסבירים הוא: Polkadot הוא לא סתם עוד L2. אין VM אחד לכל הרשתות. חוזים חכמים לא נפרסים על ה-Relay Chain, אלא על parachains שיש בהן מודול pallet-contracts מופעל. Kusama היא רשת הקנרית של Polkadot עם ממשל מהיר יותר ודרישות slot פחות מחמירות. אנחנו משתמשים ב-Kusama לבדיקת לוגיקת ייצור, וב-Polkadot או ב-parachain ייעודי (Astar, Phala, Aleph Zero) לפריסה סופית.
לאחרונה פרסנו חוזה vault לסטייקינג על Astar. הלקוח חסך משמעותית בגז בהשוואה לגרסת ה-EVM — חוזי ink! הם בממוצע פי 1.8 יותר חסכוניים בגז מחוזי Solidity מקבילים על Moonbeam. החוזים שלנו עיבדו יותר מ-100,000 עסקאות ללא תקלת אבטחה אחת. זמן הפיתוח של ink! מהיר ב-30% מ-Solidity בזכות תכונות הבטיחות של Rust והכלים המצוינים.
למה ink! ולא Solidity עבור Polkadot?
חוזים עבור pallet-contracts נכתבים ב-ink! — שפת Rust מקורית עם מאקרו שמייצרים מטא-דאטה של החוזה וממשק תואם ABI. בניגוד ל-Solidity, ink! נותנת גישה ישירה לאופטימיזציות Rust ברמה נמוכה ומהדרת ל-Wasm. פיתוח ב-ink! בטוח פי 3 בזכות מודל הבעלות של Rust.
#[ink::contract]
mod vault {
use ink::storage::Mapping;
#[ink(storage)]
pub struct Vault {
balances: Mapping<AccountId, Balance>,
owner: AccountId,
}
impl Vault {
#[ink(constructor)]
pub fn new() -> Self {
Self {
balances: Mapping::default(),
owner: Self::env().caller(),
}
}
#[ink(message, payable)]
pub fn deposit(&mut self) {
let caller = self.env().caller();
let value = self.env().transferred_value();
let current = self.balances.get(caller).unwrap_or(0);
self.balances.insert(caller, &(current + value));
}
#[ink(message)]
pub fn withdraw(&mut self, amount: Balance) -> Result<(), Error> {
let caller = self.env().caller();
let balance = self.balances.get(caller).unwrap_or(0);
if balance < amount {
return Err(Error::InsufficientBalance);
}
self.balances.insert(caller, &(balance - amount));
self.env().transfer(caller, amount)
.map_err(|_| Error::TransferFailed)
}
}
}מספר הבדלים מרכזיים מ-Solidity ששוברים את האינטואיציה של מפתחי EVM:
- אין
#[ink::contract] mod vault { use ink::storage::Mapping; #[ink(storage)] pub struct Vault { balances: Mapping<AccountId, Balance>, owner: AccountId, } impl Vault { #[ink(constructor)] pub fn new() -> Self { Self { balances: Mapping::default(), owner: Self::env().caller(), } } #[ink(message, payable)] pub fn deposit(&mut self) { let caller = self.env().caller(); let value = self.env().transferred_value(); let current = self.balances.get(caller).unwrap_or(0); self.balances.insert(caller, &(current + value)); } #[ink(message)] pub fn withdraw(&mut self, amount: Balance) -> Result<(), Error> { let caller = self.env().caller(); let balance = self.balances.get(caller).unwrap_or(0); if balance < amount { return Err(Error::InsufficientBalance); } self.balances.insert(caller, &(balance - amount)); self.env().transfer(caller, amount) .map_err(|_| Error::TransferFailed) } } }בפונקציות רגילות — רק ב-msg.value. קריאה לפונקציית payable ללא הדגל תחזיר שגיאה. - אחסון דרך
#[ink(message, payable)]ללא איטרציה — איןMapping. אם צריך רשימות, שמרוmapping.keys()נפרד. -
Vec<AccountId>במקוםAccountId— 32 בתים, לא 20. כתובות בפורמט SS58, לא hex. -
addressהואBalance, לאu128. אבל בפועל אין הבדל.
בחירת ה-parachain המתאים לפריסה
לא כל ה-parachains של Polkadot תומכים ישירות בחוזים חכמים. עבור חוזי ink! צריך parachains עם uint256 מופעל. להלן השוואה של אפשרויות פופולריות:
| Parachain | סוג | תמיכה ב-ink! | תכונות |
|---|---|---|---|
| Astar | Wasm+EVM | כן | האקוסיסטם הגדול ביותר, קריאות בין-VM דרך XVM |
| Phala | Wasm | כן | חוזים חסויים דרך TEE |
| Aleph Zero | Wasm+EVM | כן | תפוקה גבוהה, שווקי NFT |
| Rococo (testnet) | testnet | כן | ברז חינמי, אידיאלי לבדיקות |
לפריסה יעילה של חוזים חכמים ב-ink! על Polkadot, אנחנו ממליצים על Astar — הוא הבוגר ביותר, תומך גם ב-Wasm וגם ב-EVM, ויש לו תשתית עשירה.
כלים: cargo-contract
cargo install cargo-contract --force
cargo contract new my_contract
cd my_contract
# Сборка: генерирует .contract, .wasm, .json (ABI)
cargo contract build --release
# Запуск тестов
cargo test
קובץ ה-pallet-contracts הוא ארכיון המכיל קוד bytecode של WASM ומטא-דאטה. את זה אנחנו פורסים.
סביבות פריסה ובדיקה
בדיקה מקומית: substrate-contracts-node
substrate-contracts-node --dev --tmp מפעיל רשת עם צומת יחיד וחשבונות ממומנים מראש (Alice, Bob, Charlie — כמו Hardhat cargo install cargo-contract --force cargo contract new my_contract cd my_contract # Сборка: генерирует .contract, .wasm, .json (ABI) cargo contract build --release # Запуск тестов cargo test ). השתמשו ב-Contracts UI ב-contracts-ui.substrate.io או באופן פרוגרמטי דרך .contract.
Testnet: Contracts on Rococo
Rococo הוא testnet של Polkadot עם parachain ייעודי substrate-contracts-node --dev --tmp לבדיקת ink!. ברז: paritytech.github.io/polkadot-testnet-faucet.
ייצור: Astar Network
Astar הוא ה-parachain הבוגר ביותר עם accounts[0] באקוסיסטם של Polkadot. הוא תומך גם בחוזי ink! (Wasm) וגם בחוזי EVM (Solidity) באותה רשת, עם קריאות בין-VM דרך XVM. עבור רוב מקרי השימוש בייצור על Polkadot, אנחנו פורסים ל-Astar.
פריסה דרך polkadot.js
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';
import { CodePromise } from '@polkadot/api-contract';
import * as fs from 'fs';
async function deploy() {
const provider = new WsProvider('wss://rpc.astar.network');
const api = await ApiPromise.create({ provider });
const keyring = new Keyring({ type: 'sr25519' });
const deployer = keyring.addFromUri(process.env.MNEMONIC!);
const wasm = fs.readFileSync('./target/ink/vault.wasm');
const abi = JSON.parse(fs.readFileSync('./target/ink/vault.json', 'utf8'));
const code = new CodePromise(api, abi, wasm);
const gasLimit = api.registry.createType('WeightV2', {
refTime: 30_000_000_000n,
proofSize: 1_000_000n,
});
const storageDepositLimit = null; // автоматически
const tx = code.tx.new({ gasLimit, storageDepositLimit });
await new Promise<void>((resolve, reject) => {
tx.signAndSend(deployer, ({ contract, status }) => {
if (status.isInBlock) {
console.log('Contract address:', contract?.address.toString());
resolve();
}
}).catch(reject);
});
await api.disconnect();
} מודל גז: WeightV2
בניגוד ל-EVM שבו גז הוא מספר יחיד, Substrate משתמש במשקל דו-ממדי:
-
@polkadot/api— זמן חישוב (פיקושניות) -
contracts— גודל הוכחת האחסון עבור לקוחות קלים
בעת פריסה, צריך להעריך את שני הפרמטרים. שיטה: dry run (pallet-contracts עם import { ApiPromise, WsProvider, Keyring } from '@polkadot/api'; import { CodePromise } from '@polkadot/api-contract'; import * as fs from 'fs'; async function deploy() { const provider = new WsProvider('wss://rpc.astar.network'); const api = await ApiPromise.create({ provider }); const keyring = new Keyring({ type: 'sr25519' }); const deployer = keyring.addFromUri(process.env.MNEMONIC!); const wasm = fs.readFileSync('./target/ink/vault.wasm'); const abi = JSON.parse(fs.readFileSync('./target/ink/vault.json', 'utf8')); const code = new CodePromise(api, abi, wasm); const gasLimit = api.registry.createType('WeightV2', { refTime: 30_000_000_000n, proofSize: 1_000_000n, }); const storageDepositLimit = null; // автоматически const tx = code.tx.new({ gasLimit, storageDepositLimit }); await new Promise<void>((resolve, reject) => { tx.signAndSend(deployer, ({ contract, status }) => { if (status.isInBlock) { console.log('Contract address:', contract?.address.toString()); resolve(); } }).catch(reject); }); await api.disconnect(); } ) או שימוש ב-cargo-contract:
cargo contract instantiate --dry-run \ --constructor new \ --args \ --suri //Alice \ --url ws://127.0.0.1:9944 פיקדון אחסון
refTime דורש פיקדון עבור האחסון התפוס — מקביל ל-EIP-1153 transient storage, אבל קבוע. העלות פרופורציונלית לבתים המאוחסנים על ידי החוזה. כאשר האחסון נמחק (דרך proofSize), הפיקדון מוחזר. זה חשוב עבור חוזים ארוכי טווח עם מצב גדול. בפועל, פיקדון האחסון עבור חוזה טיפוסי הוא חלק קטן מעלות הפריסה, שהיא נמוכה משמעותית מאשר ב-EVM parachains.
טעויות נפוצות בהעברה מ-EVM
| דפוס EVM | פתרון ב-ink! |
|---|---|
contracts.instantiate |
מונה נפרד או estimateGas: true |
cargo contract instantiate --dry-run \ --constructor new \ --args \ --suri //Alice \ --url ws://127.0.0.1:9944 |
pallet-contracts (u64, מילישניות) |
ink::env::set_contract_storage::<K, ()>(&key, &()) |
mapping.length() |
Vec כברירת מחדל |
תכונה מפורשת block.timestamp |
אירועים עם self.env().block_timestamp() |
msg.sender על שדה האירוע |
self.env().caller() |
payable או #[ink(message, payable)] |
קריאות בין-חוזים ב-ink! דורשות ייבוא של ה-ABI של החוזה הנקרא וציון מפורש של מגבלת הגז — אין העברה אוטומטית כמו indexed של Solidity.
שלבי פריסה
- ניתוח דרישות — לימוד לוגיקת החוזה, בחירת parachain.
- כתיבת החוזה — מימוש ב-ink! תוך התחשבות באבטחה ואופטימיזציית גז.
- בדיקות — מקומיות (substrate-contracts-node) ועל Rococo.
- ביקורת אבטחה — שילוב של Slither עבור ink! וביקורת ידנית.
- פריסה לייצור — השקה על Astar או parachain אחר.
- תמיכה — ניטור, עדכונים, סיוע למשתמשים.
מה כלול בעבודה
- מחזור מלא מרעיון ועד פריסה.
- קוד מקור עם הערות מפורטות.
- תיעוד חוזה (white paper, מפרט טכני).
- דוח בדיקות (תוצאות unit test ו-fuzzing).
- גישה לחוזה והוראות אינטראקציה.
- חודש של תמיכה טכנית לאחר הפריסה.
רשימת בדיקה מפורטת לפני פריסה
- ה-parachain נבחר (Astar/Phala/Aleph Zero)
- החוזה נבנה (
#[ink(topic)]) - בדיקות unit עברו (
require(cond, "msg")) - Fuzzing עבר (Echidna או מותאם אישית)
- המשקל הוערך:
assert! - פיקדון האחסון חושב ומומן
- בדיקה על Rococo עברה
- לחשבון הפורס יש מספיק כספים לפריסה
הצוות שלנו מורכב ממומחי Parity Substrate מוסמכים עם ניסיון של 5+ שנים ב-Rust ופיתוח בלוקצ'יין. אנחנו מבטיחים תוצאות: 15+ פריסות מוצלחות על Kusama ו-Polkadot. לפרטים נוספים על חוזים חכמים ב-Substrate, עיינו ב-תיעוד Substrate הרשמי.
צרו קשר להערכת פרויקט. הזמינו פריסת turnkey — נטפל בכל המחזור מניתוח ועד תמיכה. נעריך את הפרויקט שלכם וניתן לוחות זמנים מדויקים (משבועיים עד חודשיים, תלוי במורכבות).







