Разработка сервиса блокчейн-доменов

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Разработка сервиса блокчейн-доменов
Сложный
от 1 недели до 3 месяцев
Часто задаваемые вопросы

Направления блокчейн-разработки

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1285
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    902
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1121
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    857

Разработка сервиса блокчейн-доменов

Блокчейн-домены решают проблему, которая в Web3 стоит особенно остро: криптографические адреса нечитаемы для человека. 0x742d35Cc6634C0532925a3b844Bc454e4438f44e — это не адрес, а источник ошибок. Blockchain domain service заменяет адрес на человекочитаемое имя, одновременно превращая это имя в portable identity record.

Архитектура DNS-like системы на блокчейне

Namespace и registry

Центральный компонент — Registry контракт. Хранит маппинг от хешированного имени (namehash) к owner и resolver адресу. ENS использует именно эту архитектуру, и она оправдана: разделение ownership (Registry) и data storage (Resolver) позволяет менять resolver без потери ownership.

contract DomainRegistry {
    struct Record {
        address owner;
        address resolver;
        uint64 ttl;
    }
    
    // namehash => Record
    mapping(bytes32 => Record) private records;
    
    // namehash => operator => approved
    mapping(bytes32 => mapping(address => bool)) private operators;
    
    event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
    event Transfer(bytes32 indexed node, address owner);
    event NewResolver(bytes32 indexed node, address resolver);
    
    function setOwner(bytes32 node, address _owner) external authorised(node) {
        records[node].owner = _owner;
        emit Transfer(node, _owner);
    }
    
    function setSubnodeOwner(
        bytes32 node,
        bytes32 label,
        address _owner
    ) external authorised(node) returns (bytes32) {
        bytes32 subnode = keccak256(abi.encodePacked(node, label));
        records[subnode].owner = _owner;
        emit NewOwner(node, label, _owner);
        return subnode;
    }
    
    modifier authorised(bytes32 node) {
        address owner = records[node].owner;
        require(
            owner == msg.sender || operators[node][msg.sender],
            "Not authorised"
        );
        _;
    }
}

Namehash алгоритм

Имена преобразуются в bytes32 через рекурсивный хеш. alice.mynskeccak256(keccak256('' bytes32(0)) + keccak256('myns')) → keccak256(result + keccak256('alice')). Это позволяет вычислять хеш любого уровня вложенности, не зная полного имени — только его компонентов.

import { ethers } from "ethers";

function namehash(name: string): string {
  let node = "0x" + "0".repeat(64);
  
  if (name === "") return node;
  
  const labels = name.split(".").reverse();
  for (const label of labels) {
    node = ethers.utils.keccak256(
      ethers.utils.concat([
        node,
        ethers.utils.keccak256(ethers.utils.toUtf8Bytes(label))
      ])
    );
  }
  
  return node;
}

Resolver контракт

Resolver хранит данные, ассоциированные с именем. Один resolver может обслуживать множество имён.

contract PublicResolver {
    DomainRegistry immutable registry;
    
    // node => coinType => address (EIP-2304: multi-chain addresses)
    mapping(bytes32 => mapping(uint256 => bytes)) private _addresses;
    
    // node => key => value (текстовые записи)
    mapping(bytes32 => mapping(string => string)) private _textRecords;
    
    // node => contenthash (IPFS/Swarm/Arweave)
    mapping(bytes32 => bytes) private _contenthash;
    
    event AddressChanged(bytes32 indexed node, uint256 coinType, bytes newAddress);
    event TextChanged(bytes32 indexed node, string indexed key, string value);
    event ContenthashChanged(bytes32 indexed node, bytes hash);
    
    // Ethereum address (coinType 60 = ETH)
    function setAddr(bytes32 node, address addr) external authorised(node) {
        setAddr(node, 60, addressToBytes(addr));
    }
    
    // Multi-chain: BTC coinType = 0, ETH = 60, SOL = 501
    function setAddr(bytes32 node, uint256 coinType, bytes calldata a) 
        public authorised(node) 
    {
        _addresses[node][coinType] = a;
        emit AddressChanged(node, coinType, a);
    }
    
    // Текстовые записи: "email", "url", "avatar", "description", "twitter"
    function setText(bytes32 node, string calldata key, string calldata value) 
        external authorised(node) 
    {
        _textRecords[node][key] = value;
        emit TextChanged(node, key, value);
    }
    
    // IPFS contenthash для децентрализованных сайтов
    function setContenthash(bytes32 node, bytes calldata hash) external authorised(node) {
        _contenthash[node] = hash;
        emit ContenthashChanged(node, hash);
    }
    
    modifier authorised(bytes32 node) {
        require(registry.owner(node) == msg.sender, "Not authorised");
        _;
    }
}

Registrar контракт и NFT

Имена верхнего уровня (TLD) регистрируются через Registrar. Каждое зарегистрированное имя — ERC-721 NFT, что позволяет торговать именами на OpenSea и других маркетплейсах.

contract BaseRegistrar is ERC721 {
    DomainRegistry public registry;
    bytes32 public baseNode; // namehash TLD (e.g., namehash("myns"))
    
    mapping(uint256 => uint256) public expiries; // tokenId => expiry timestamp
    
    uint256 public constant GRACE_PERIOD = 90 days;
    
    function available(uint256 id) public view returns (bool) {
        return expiries[id] + GRACE_PERIOD < block.timestamp;
    }
    
    function register(
        uint256 id,
        address owner,
        uint256 duration
    ) external onlyController returns (uint256) {
        require(available(id), "Not available");
        
        expiries[id] = block.timestamp + duration;
        
        if (_exists(id)) {
            // Если токен существовал раньше — просто обновляем expiry
            _transfer(address(0), owner, id); // re-issue
        } else {
            _mint(owner, id);
        }
        
        registry.setSubnodeOwner(baseNode, bytes32(id), owner);
        return expiries[id];
    }
    
    function renew(uint256 id, uint256 duration) external onlyController returns (uint256) {
        require(expiries[id] + GRACE_PERIOD >= block.timestamp, "Expired");
        expiries[id] += duration;
        return expiries[id];
    }
}

Price Oracle и регистрация

Цены регистрации обычно зависят от длины имени:

contract PriceOracle {
    // Цена в USD/год, в wei через Chainlink ETH/USD feed
    uint256[5] public rentPrices = [
        160e18, // 1 символ: $160/год
        40e18,  // 2 символа: $40/год
        10e18,  // 3 символа: $10/год
        5e18,   // 4 символа: $5/год
        1e18    // 5+ символов: $1/год
    ];
    
    AggregatorV3Interface public immutable usdOracle;
    
    function price(string calldata name, uint256 duration) 
        external view returns (uint256 weiAmount) 
    {
        uint256 len = strlen(name);
        uint256 usdPrice = rentPrices[min(len - 1, 4)];
        uint256 annualUsd = usdPrice * duration / 365 days;
        
        (, int256 usdEthPrice,,,) = usdOracle.latestRoundData();
        return annualUsd * 1e8 / uint256(usdEthPrice); // 8 decimal Chainlink feed
    }
}

Reverse Resolution

Forward resolution: alice.myns → 0x742d.... Reverse resolution: 0x742d... → alice.myns. Это необходимо для отображения имён в интерфейсах.

Реализуется через специальный reverse namespace: адрес 0x742d... маппируется на запись 742d...addr.reverse. Пользователь сам устанавливает reverse record — это его выбор, какое имя показывать.

contract ReverseRegistrar {
    bytes32 constant ADDR_REVERSE_NODE = 
        0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
    
    function setName(string calldata name) external returns (bytes32) {
        bytes32 node = claimWithResolver(msg.sender, address(defaultResolver));
        defaultResolver.setName(node, name);
        return node;
    }
    
    function node(address addr) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(ADDR_REVERSE_NODE, sha3HexAddress(addr)));
    }
}

Subdomain делегирование

Мощная фича: владелец домена может создавать субдомены и делегировать их. team.alice.myns, dao.alice.myns — создаются одной транзакцией. Протоколы используют это для on-chain identity системы участников.

NameWrapper (паттерн ENS v2) — обёртка, которая превращает субдомены в ERC-1155 токены и добавляет permission system: fuses. Fuse "CANNOT_TRANSFER" — имя нельзя передать (soulbound subdomain). Fuse "CANNOT_CREATE_SUBDOMAIN" — нельзя создать субдомены второго уровня.

Offchain resolver (CCIP-Read / EIP-3668)

Для масштабируемости — off-chain хранение данных с on-chain верификацией. Resolver возвращает ошибку OffchainLookup с URL и данными запроса. Клиент делает запрос к off-chain gateway, получает подписанный ответ, передаёт его обратно в контракт для верификации подписи.

Это снижает стоимость записи данных с on-chain gas до off-chain storage. Подходит для profilge данных, большого количества текстовых записей.

Стек и интеграция

Компонент Технология
Smart contracts Solidity 0.8.x + OpenZeppelin
Chainlink Oracle AggregatorV3Interface для ETH/USD
Frontend resolution ethers.js provider.resolveName()
Indexing The Graph subgraph
CCIP-Read gateway Node.js сервер + ECDSA подпись

Сроки разработки

Базовый сервис (Registry + Resolver + Registrar + Price Oracle): 6-8 недель.

Расширенный (NameWrapper + reverse resolution + CCIP-Read gateway + marketplace интеграция): 10-14 недель.

Аудит обязателен — Registrar управляет ETH payments. 2-4 недели дополнительно.