Деплой смарт-контрактов в Cosmos (CosmWasm)
CosmWasm — это не EVM. Если вы приходите с опытом Solidity, первое, что сбивает с толку: контракты написаны на Rust, компилируются в WebAssembly, и взаимодействуют через message-passing, а не через прямые вызовы функций. Архитектурная модель ближе к actor model, чем к вызовам методов объекта. Это важно понять до написания первой строки кода.
Среда разработки и toolchain
Для работы с CosmWasm нужен Rust с таргетом wasm32-unknown-unknown:
rustup target add wasm32-unknown-unknown
cargo install cosmwasm-check # валидация скомпилированного wasm
cargo install cargo-generate # шаблоны проектов
Стандартный шаблон проекта через cw-template:
cargo generate --git https://github.com/CosmWasm/cw-template.git --name my-contract
Структура CosmWasm контракта
Контракт состоит из трёх обязательных entry points:
// src/contract.rs
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response, ContractError> { ... }
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> { ... }
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(
deps: Deps,
env: Env,
msg: QueryMsg,
) -> StdResult<Binary> { ... }
Ключевое отличие от Solidity: DepsMut (для записи) и Deps (только чтение) явно разделены. Случайно записать в storage из query-функции физически невозможно — компилятор не даст.
State management через cw-storage-plus:
use cw_storage_plus::{Item, Map};
pub const CONFIG: Item<Config> = Item::new("config");
pub const BALANCES: Map<&Addr, Uint128> = Map::new("balances");
Map с адресом как ключом — стандартный паттерн для хранения балансов. Нет mapping(address => uint256) из Solidity, но семантика та же.
Компиляция и оптимизация
Raw cargo build --target wasm32-unknown-unknown --release даёт бинарник с debug символами и неоптимизированным размером. Для продакшна используем официальный оптимизатор:
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/rust-optimizer:0.15.0
Результат — artifacts/my_contract.wasm. Размер должен быть < 800KB (hard limit большинства сетей). Проверка:
cosmwasm-check artifacts/my_contract.wasm
Деплой на Cosmos-совместимую сеть
Покажу на примере Neutron (одна из основных CosmWasm-сетей для DeFi). Используем neutrond CLI или CosmJS:
Через CLI:
# Store — загрузка bytecode в сеть
neutrond tx wasm store artifacts/my_contract.wasm \
--from <wallet> \
--gas auto --gas-adjustment 1.3 \
--fees 5000untrn \
--chain-id neutron-1 \
--node https://rpc-lb.neutron.org:443
# Получаем code_id из events транзакции
# Instantiate — создание инстанса контракта
neutrond tx wasm instantiate <CODE_ID> \
'{"admin": "neutron1...", "param": "value"}' \
--label "my-contract-v1" \
--admin <wallet_address> \
--from <wallet> \
--gas auto --gas-adjustment 1.3 \
--fees 5000untrn \
--chain-id neutron-1
Через CosmJS (для автоматизации):
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, {
prefix: "neutron",
});
const client = await SigningCosmWasmClient.connectWithSigner(
"https://rpc-lb.neutron.org:443",
wallet
);
// Upload
const uploadResult = await client.upload(senderAddress, wasmBinary, "auto");
const codeId = uploadResult.codeId;
// Instantiate
const { contractAddress } = await client.instantiate(
senderAddress,
codeId,
{ admin: senderAddress },
"my-contract-v1",
"auto"
);
Мигрируемые контракты
CosmWasm имеет встроенный механизм миграции — аналог upgradeable proxy в EVM, но проще. Нужно добавить entry point:
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(
deps: DepsMut,
env: Env,
msg: MigrateMsg,
) -> Result<Response, ContractError> {
// Логика миграции state если нужна
Ok(Response::default())
}
При деплое инстанса с --admin адресом, этот адрес может вызвать MigrateContract с новым code_id. Без --admin — контракт immutable. Для production протоколов admin должен быть multisig (через cosmwasm-multisig или DAO DAO).
Ориентиры по срокам
Контракт с готовой логикой, написанный опытным Rust-разработчиком: деплой и верификация 4–8 часов. Если нужна разработка с нуля или портирование с Solidity — 1–2 дня в зависимости от сложности.







