Деплой смарт-контрактов в Near Protocol

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Деплой смарт-контрактов в Near Protocol
Средняя
от 4 часов до 2 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Деплой смарт-контрактов в Near Protocol

Разработчики, приходящие из EVM-экосистемы, обычно ждут чего-то похожего на Solidity + Hardhat. Near работает иначе: контракты — это WebAssembly-модули, аккаунт-модель принципиально отличается от Ethereum, а хранилище данных платит сам разработчик, а не пользователь. Эти отличия нужно понять до написания первой строки кода, иначе деплой превратится в серию неприятных сюрпризов.

Среда выполнения и языки

Near VM исполняет WASM-байткод. Официально поддерживаются два языка:

Rust + near-sdk-rs — основной выбор для production. SDK предоставляет макросы #[near_bindgen], #[init], BorshSerialize/BorshDeserialize для сериализации состояния. Borsh (Binary Object Representation Serializer for Hashing) — детерминированный бинарный формат, обязательный для хранения состояния контрактов Near.

JavaScript/TypeScript + near-sdk-js — для прототипов и несложной логики. Компилируется в WASM через QuickJS, производительность заметно ниже, лимиты по gas жёстче.

Пример минимального Rust-контракта:

use near_sdk::{near_bindgen, env, AccountId};
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};

#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct Counter {
    value: i64,
}

#[near_bindgen]
impl Counter {
    #[init]
    pub fn new() -> Self {
        Self { value: 0 }
    }

    pub fn increment(&mut self) {
        self.value += 1;
    }

    pub fn get(&self) -> i64 {
        self.value
    }
}

Аккаунт-модель и хранилище

Ключевое отличие от EVM: в Near каждый смарт-контракт живёт на именованном аккаунте (myapp.near, myapp.testnet). Аккаунт хранит код контракта и его состояние. Стоимость хранения — 1 NEAR за 100 КБ состояния, и эти средства блокируются на балансе аккаунта. Это называется storage staking.

Практическое следствие: если контракт хранит данные пользователей, нужна логика storage_deposit — пользователь вносит NEAR-депозит перед регистрацией, эти средства покрывают его storage. Стандарт NEP-145 определяет этот паттерн для fungible tokens.

#[payable]
pub fn storage_deposit(&mut self, account_id: Option<AccountId>) -> StorageBalance {
    let amount = env::attached_deposit();
    let account_id = account_id.unwrap_or_else(env::predecessor_account_id);
    // минимум 0.00125 NEAR за запись аккаунта
    assert!(amount >= STORAGE_PER_ACCOUNT, "Insufficient deposit");
    // ...
}

Sub-аккаунты — стандартный паттерн для factory-контрактов: token.myapp.near, pool.myapp.near. Каждый sub-аккаунт — полноценный независимый аккаунт.

Кросс-контрактные вызовы и async-модель

Near — асинхронный шардированный блокчейн. Кросс-контрактный вызов не выполняется синхронно внутри транзакции — он создаёт отдельный receipt, который обрабатывается в следующем блоке. Результат приходит в callback.

#[near_bindgen]
impl MyContract {
    pub fn call_external(&mut self, contract_id: AccountId) -> Promise {
        ext_other_contract::ext(contract_id)
            .with_static_gas(Gas(5 * TGAS))
            .get_value()
            .then(
                Self::ext(env::current_account_id())
                    .with_static_gas(Gas(5 * TGAS))
                    .on_value_received()
            )
    }

    #[private]
    pub fn on_value_received(&mut self, #[callback_result] result: Result<u64, PromiseError>) {
        match result {
            Ok(value) => { /* обработка */ }
            Err(_) => { env::panic_str("External call failed") }
        }
    }
}

#[private] — макрос, добавляющий проверку assert_eq!(env::current_account_id(), env::predecessor_account_id()). Без него любой может вызвать callback напрямую.

Сборка и деплой

Инструментарий: cargo-near — официальный CLI для сборки, near-cli-rs (Rust-переписанная версия) для деплоя.

# Установка
cargo install cargo-near
cargo install near-cli-rs

# Сборка оптимизированного WASM
cargo near build

# Деплой на testnet
near contract deploy myapp.testnet \
  use-file ./target/near/myapp.wasm \
  without-init-call \
  network-config testnet \
  sign-with-keychain \
  send

WASM-файл после сборки с cargo-near проходит через wasm-opt (Binaryen) автоматически — это уменьшает размер и экономит на storage.

Для mainnet деплой через multisig-аккаунт или через near-cli с hardware wallet (--useLedgerKey). Деплой без инициализации — контракт в неинициализированном состоянии, первый вызов new() задаёт начальное состояние.

Тестирование

Unit-тесты — стандартные Rust unit tests с VMContextBuilder для мока окружения (predecessor, attached_deposit, block_timestamp).

Workspaces-rs — интеграционные тесты, запускают локальный sandbox Near и деплоят реальный WASM. Это де-факто стандарт для тестирования кросс-контрактных взаимодействий:

#[tokio::test]
async fn test_full_flow() -> anyhow::Result<()> {
    let worker = near_workspaces::sandbox().await?;
    let wasm = std::fs::read("./target/near/myapp.wasm")?;
    let contract = worker.dev_deploy(&wasm).await?;
    
    let result = contract.call("increment")
        .transact().await?;
    assert!(result.is_success());
    Ok(())
}

Типичные ошибки при переносе с EVM

  • Нет msg.sender как адреса. В Near env::predecessor_account_id() — строка, не 20 байт. Сравнение через == для AccountId.
  • Паника вместо revert. env::panic_str("message") или assert!() — аналог require() в Solidity. Стоимость газа за панику не возвращается.
  • Итерация по LookupMap. LookupMap не итерируемый. Для итерируемых коллекций — UnorderedMap, Vector. Выбор структуры данных влияет на gas.
  • Gas единицы. 1 TGas = 10^12 gas. Простой вызов ~2-3 TGas, кросс-контрактный +5 TGas минимум. Лимит транзакции — 300 TGas.

Процесс работы

Аудит требований → выбор языка (Rust/JS) → проектирование storage-модели (критично для cost) → разработка с unit-тестами → интеграционное тестирование через workspaces → деплой на testnet → верификация через Near Explorer → деплой на mainnet.

Срок 4 часа — это деплой готового кода. Разработка контракта с нуля с тестированием: 1-5 дней в зависимости от сложности логики.