Розроблення системи токенізації AI-моделей
Токенізація AI-моделей — це не просто «обернути модель в NFT». Це повнофункціональна економічна інфраструктура: права на використання, моделі доходу для творців, перевірка виходу on-chain та механізми управління версіями моделей. Завдання складне, тому що AI-модель — це не статичний актив, як зображення, а живий артефакт із ваговими параметрами, версіями, форками fine-tune та витратами на обчислювальний висновок.
Ринок рухається в бік децентралізованих AI-маркетплейсів: Bittensor, Ritual, Gensyn, Hyperbolic — різні підходи до однієї проблеми. Але більшість команд будує токенізацію поверх них або самостійно, під конкретну вертикаль (медицина, фінанси, генерація контенту).
Архітектура: що саме ми токенізуємо
Перед написанням смарт-контрактів потрібно відповісти на питання: що є токенізованим активом?
Варіанти:
- Ваги моделей — самі параметри (checkpoint), зберігаються off-chain (IPFS, Arweave, Filecoin), on-chain — хеш та метаданні
- Права на висновок — доступ до API обчислень, не до ваг
- Права на fine-tune — можливість створити похідну модель від базової
- Доля в доходах моделі — токен розподілу доходу, який не дає прямого доступу до ваг
У більшості виробничих випадків токенізуються саме права на висновок плюс опціонально розподіл доходів. Ваги публічно доступні рідко (це IP творця).
Зберігання ваг та перевірка цілісності
contract AIModelRegistry {
struct ModelVersion {
bytes32 weightsHash; // SHA-256 хеш файлу контрольної точки
string storageURI; // ipfs://... або ar://...
uint256 parameterCount; // кількість параметрів (для ціноутворення)
string architecture; // "llama-3-8b", "stable-diffusion-xl"
uint256 registeredAt;
address creator;
bool active;
}
struct InferenceToken {
uint256 modelId;
uint256 versionId;
uint256 callsRemaining; // обмеження викликів
uint256 expiresAt; // часове обмеження
bool transferable;
address holder;
}
mapping(uint256 => ModelVersion[]) public modelVersions;
mapping(uint256 => InferenceToken) public inferenceTokens;
uint256 private _modelCounter;
uint256 private _tokenCounter;
event ModelRegistered(uint256 indexed modelId, address creator, bytes32 weightsHash);
event InferenceTokenMinted(uint256 indexed tokenId, uint256 modelId, address holder);
function registerModel(
bytes32 weightsHash,
string calldata storageURI,
uint256 parameterCount,
string calldata architecture
) external returns (uint256 modelId) {
modelId = ++_modelCounter;
modelVersions[modelId].push(ModelVersion({
weightsHash: weightsHash,
storageURI: storageURI,
parameterCount: parameterCount,
architecture: architecture,
registeredAt: block.timestamp,
creator: msg.sender,
active: true
}));
emit ModelRegistered(modelId, msg.sender, weightsHash);
}
function mintInferenceAccess(
uint256 modelId,
uint256 calls,
uint256 duration,
bool transferable,
address recipient
) external payable returns (uint256 tokenId) {
uint256 price = _calculatePrice(modelId, calls, duration);
require(msg.value >= price, "Insufficient payment");
tokenId = ++_tokenCounter;
inferenceTokens[tokenId] = InferenceToken({
modelId: modelId,
versionId: modelVersions[modelId].length - 1,
callsRemaining: calls,
expiresAt: block.timestamp + duration,
transferable: transferable,
holder: recipient
});
emit InferenceTokenMinted(tokenId, modelId, recipient);
}
}
Перевірка висновків моделей on-chain: zkML
Найскладніша частина системи — довести, що конкретний вихід дійсно отриманий від конкретної моделі з конкретними вагами без перерахунку висновку on-chain (неможливо при будь-якому розумному розмірі моделі).
Рішення — zkML (zero-knowledge machine learning). Генерується ZK-доказ того, що обчислення виконано правильно, доказ перевіряється on-chain.
Стек zkML
| Фреймворк | Підхід | Обмеження | Зрілість |
|---|---|---|---|
| ezkl | PLONK circuit із ONNX | Моделі до ~100M параметрів | Production |
| RISC Zero | zkVM, будь-який Rust код | Висока вартість доказу | Production |
| Modulus Labs | Користувацькі circuit | Потрібна партнерство | Beta |
| Giza | Starknet-орієнтований | Обмежена екосистема | Alpha |
ezkl — найпрактичніший вибір для більшості завдань:
import ezkl
import torch
import json
# Експортувати модель у ONNX
model = YourModel()
model.eval()
dummy_input = torch.randn(1, 128)
torch.onnx.export(model, dummy_input, "model.onnx", opset_version=11)
# Налаштування ezkl
settings = ezkl.PyRunArgs()
settings.input_visibility = "public"
settings.output_visibility = "public"
settings.param_visibility = "fixed" # ваги фіксовані в circuit
await ezkl.gen_settings("model.onnx", "settings.json", py_run_args=settings)
await ezkl.calibrate_settings("input.json", "model.onnx", "settings.json", "resources")
# Компілювання circuit
await ezkl.compile_circuit("model.onnx", "circuit.compiled", "settings.json")
# Генерування ключів
await ezkl.setup("circuit.compiled", "vk.key", "pk.key")
# Генерування свідка та доказу
await ezkl.gen_witness("input.json", "circuit.compiled", "witness.json")
await ezkl.prove("witness.json", "circuit.compiled", "pk.key", "proof.json")
# Перевірка (те ж саме на-chain)
result = await ezkl.verify("proof.json", "settings.json", "vk.key")
print(f"Proof valid: {result}")
Для перевірки on-chain ezkl генерує верифікатор Solidity:
ezkl create-evm-verifier \
--vk-path vk.key \
--settings-path settings.json \
--sol-code-path verifier.sol \
--abi-path verifier.abi
Генерований verifier.sol розгортається як окремий контракт. Основний реєстр викликає його під час кожного доказу висновку on-chain.
Управління версіями та форки моделей
AI-моделі живуть та еволюціонують. Вам потрібен механізм on-chain для керування версіями та похідними моделями (fine-tunes).
Граф деривативів
contract ModelDerivativeGraph {
struct DerivativeRelation {
uint256 parentModelId;
uint256 parentVersionId;
uint256 royaltyBps; // базисні пункти роялті для батьківської моделі
bool requiresApproval; // потрібне схвалення від творця базової моделі
bool approved;
}
// childModelId => relation
mapping(uint256 => DerivativeRelation) public derivatives;
// Реєстр роялті: при кожному висновку похідної моделі,
// % йде на адресу творця базової моделі
function registerFineTune(
uint256 childModelId,
uint256 parentModelId,
uint256 parentVersionId,
uint256 royaltyBps
) external {
ModelVersion memory parent = registry.getVersion(parentModelId, parentVersionId);
// Якщо базова модель потребує схвалення — встановити прапор
bool needsApproval = parentModelConfig[parentModelId].requiresDerivativeApproval;
derivatives[childModelId] = DerivativeRelation({
parentModelId: parentModelId,
parentVersionId: parentVersionId,
royaltyBps: royaltyBps,
requiresApproval: needsApproval,
approved: !needsApproval
});
if (!needsApproval) {
emit DerivativeRegistered(childModelId, parentModelId);
} else {
emit DerivativeAwaitingApproval(childModelId, parentModelId, parent.creator);
}
}
function distributeInferenceRevenue(uint256 modelId, uint256 amount) internal {
// Підняти по дереву деривативів та розподілити роялті
uint256 currentModel = modelId;
uint256 remaining = amount;
while (derivatives[currentModel].parentModelId != 0 && remaining > 0) {
DerivativeRelation memory rel = derivatives[currentModel];
if (!rel.approved) break;
uint256 royalty = remaining * rel.royaltyBps / 10000;
address parentCreator = registry.getCreator(rel.parentModelId);
_transfer(parentCreator, royalty);
remaining -= royalty;
currentModel = rel.parentModelId;
}
// Залишок — творцю листяної моделі
_transfer(registry.getCreator(modelId), remaining);
}
}
Ціноутворення висновків
Вартість виклику моделі залежить від кількох параметрів. Простої лінійної залежності працює погано — різні запити до однієї моделі можуть відрізнятися по витратах обчислень на порядок (довжина контексту для LLM, дозвіл для дифузійних моделей).
Динамічне ціноутворення
contract InferencePricing {
struct PricingConfig {
uint256 basePricePerCall; // базова ціна за виклик
uint256 pricePerInputToken; // для LLM: ціна за input токен
uint256 pricePerOutputToken; // для LLM: ціна за output токен
uint256 pricePerMegapixel; // для моделей зображень
uint256 currency; // 0=native, 1=USDC, 2=USDT
uint256 creatorShareBps; // доля творця від доходу
uint256 platformShareBps; // доля платформи
}
mapping(uint256 => PricingConfig) public modelPricing;
function estimateCallCost(
uint256 modelId,
uint256 inputTokens,
uint256 expectedOutputTokens,
uint256 imageWidth,
uint256 imageHeight
) external view returns (uint256 totalCost) {
PricingConfig memory config = modelPricing[modelId];
totalCost = config.basePricePerCall;
totalCost += inputTokens * config.pricePerInputToken;
totalCost += expectedOutputTokens * config.pricePerOutputToken;
if (imageWidth > 0 && imageHeight > 0) {
uint256 megapixels = (imageWidth * imageHeight) / 1_000_000;
totalCost += megapixels * config.pricePerMegapixel;
}
}
}
Токен-гейтинг та доступ до моделей
Крім моделей pay-per-call, система підтримує token-gating: власники конкретного ERC-20 або ERC-721 токена отримують доступ до моделі без додаткової оплати (або зі скидкою).
Це відкриває сценарії: модель як частина NFT-колекції (кожен власник NFT отримує доступ до AI-асистента), стейкинг-базований доступ (застейкай X токенів — отримай Y викликів на місяць), DAO-контрольований whitelist.
function checkAccess(uint256 modelId, address user) public view returns (bool, uint256 remainingCalls) {
AccessPolicy memory policy = accessPolicies[modelId];
// ERC-721 token gate
if (policy.requiredNFT != address(0)) {
if (IERC721(policy.requiredNFT).balanceOf(user) > 0) {
return (true, policy.nftHolderMonthlyCallLimit);
}
}
// ERC-20 staking gate
if (policy.requiredStake > 0) {
uint256 staked = stakingVault.stakedBalance(user, policy.stakeToken);
if (staked >= policy.requiredStake) {
uint256 calls = (staked / policy.requiredStake) * policy.callsPerStakeUnit;
return (true, calls);
}
}
// Платний доступ через токени висновку
uint256 tokenId = userInferenceTokens[modelId][user];
if (tokenId != 0) {
InferenceToken memory token = inferenceTokens[tokenId];
if (token.callsRemaining > 0 && block.timestamp < token.expiresAt) {
return (true, token.callsRemaining);
}
}
return (false, 0);
}
Управління та оновлення моделей
Токенізована модель — це живий продукт. Вам потрібен механізм голосування за прийняття нових версій ваг, зміну умов доступу, керування скарбницею (доходи платформи).
Стандартна схема: ERC-20 токен управління + OpenZeppelin Governor + Timelock. AI-специфічне — пропозиції про зміни ваг повинні пройти технічний огляд (перевірка нового weightsHash, тестування на benchmark). Включення на-chain оракула результатів бенчмарку — надлишково на старті, але можливо через Chainlink.
Стек та терміни розроблення
Смарт-контракти: Solidity, OpenZeppelin, Hardhat/Foundry. 8–12 тижнів на повний реєстр із управлінням.
ZK-верифікація: ezkl для моделей до 100M параметрів, RISC Zero для довільного висновку. Підготовка circuit — 4–8 тижнів залежно від архітектури моделі.
Off-chain інфраструктура: Node.js / Python API для прийняття запитів, черги обчислень (Bull/Redis), інтеграція з GPU-провайдерами (Akash, Vast.ai, власний кластер).
Аудит: обов'язковий перед mainnet. Особливу увагу — логіка управління правами доступу та розподіл доходів.
Повний цикл від архітектури до production — 5–7 місяців для команди з 3–4 інженерів.







