Зустрічали проєкти, де віртуальна земля продається як NFT, а потім ніхто не знає, що з нею робити? Без продуманої архітектури координатної системи, гнучкої системи прав та економіки з дефіцитом карта залишається порожньою. Ми розробили десятки таких систем — від простих 2D гридів до повноцінних 3D світів. Замовте розробку virtual land під ключ — від смарт-контрактів до клієнтського рендерингу.
Основні проблеми: як зберігати координати в смарт-контрактах без божевільних газ-трат? Як об'єднувати ділянки в Estate з перевіркою суміжності? Як дати власникам права налаштування своєї землі? Нижче розберемо кожну з проблем з кодом та цифрами.
Координатна система та зберігання
Земля зазвичай представлена як 2D Grid — цілочисельні координати (x, y). Кожна ділянка — унікальний NFT з координатами як ключовим атрибутом. Упаковка координат в tokenId (x: int16, y: int16 → uint32) обмежує світ, але знижує газ.
contract VirtualLand is ERC721 { struct LandInfo { int16 x; int16 y; address owner; bool developed; uint32 districtId; } mapping(uint256 => LandInfo) public lands; mapping(bytes32 => uint256) public coordsToTokenId; function _coordsToId(int16 x, int16 y) internal pure returns (uint256) { return uint256(uint32(uint16(int16(x))) | (uint32(uint16(int16(y))) << 16)); } function _hashCoords(int16 x, int16 y) internal pure returns (bytes32) { return keccak256(abi.encodePacked(x, y)); } function mint(int16 x, int16 y, address to) external onlyMinter { bytes32 coordHash = _hashCoords(x, y); require(coordsToTokenId[coordHash] == 0, "Land already exists"); uint256 tokenId = _coordsToId(x, y); _safeMint(to, tokenId); lands[tokenId] = LandInfo({x: x, y: y, owner: to, developed: false, districtId: _getDistrictId(x, y)}); coordsToTokenId[coordHash] = tokenId; } } Estate: об'єднання ділянок
Estate — кілька сусідніх ділянок, об'єднаних в один NFT. Спрощує управління великим простором. Перевірка суміжності on-chain — дорога для великих об'єднань, тому для estates до 20–30 ділянок використовується O(n²) алгоритм, а для великих — off-chain з Merkle proof або ZK proof.
Деталі перевірки суміжності
Для on-chain перевірки можна використовувати алгоритм пошуку в глибину (DFS), обмежений за газом. Приблизний ліміт — 2000 одиниць на ділянку. Off-chain з Merkle proof дешевший у 10 разів.Як перевірити суміжність ділянок on‑chain?
Перевірка того, що набір ділянок утворює зв'язний граф, — дорога on-chain операція. Оптимізація: перевіряти, що кожна ділянка має хоча б одного сусіда в множині. Це не повна зв'язність, але достатньо для Estate. On-chain перевірка в 10 разів дорожча, ніж off-chain з ZK proof, тому для великих об'єднань використовуйте off-chain.
Система прав та операторів
Власник ділянки повинен контролювати, хто може будувати на його землі. Реалізуємо через bitmap прав: BUILD, SCRIPT, ADMIN.
uint8 public constant RIGHT_BUILD = 1 << 0; uint8 public constant RIGHT_SCRIPT = 1 << 1; uint8 public constant RIGHT_ADMIN = 1 << 3; mapping(uint256 => mapping(address => uint8)) public landOperators; function grantRights(uint256 landId, address operator, uint8 rights) external { require(ownerOf(landId) == msg.sender, "Not owner"); landOperators[landId][operator] |= rights; } function hasRight(uint256 landId, address operator, uint8 right) public view returns (bool) { return ownerOf(landId) == operator || (landOperators[landId][operator] & right) != 0; } Економіка віртуальної землі
District — адміністративна одиниця, що об'єднує ділянки. Може мати свій governance та спільний дохід. Adjacency bonus — ділянки поруч з landmarks (центр міста) дорожчі. Реалізується через зберігання landmark-координат та розрахунок відстані. Наприклад, для кожної сусідньої ділянки перевіряється наявність landmark і присвоюється бонус. Така механіка створює ажіотаж на primary sale, але без контенту земля марна. Наш досвід показує: спочатку first-party контент, потім сценарії використання.
Marketplace та торгівля
Вбудований marketplace з EIP-2981 роялті дозволяє торгувати ділянками. Газ-ефективна реалізація з перевіркою авторських прав.
struct Listing { uint256 tokenId; uint256 price; address seller; uint256 expiresAt; } mapping(uint256 => Listing) public listings; function buy(uint256 tokenId) external payable { Listing memory listing = listings[tokenId]; require(block.timestamp <= listing.expiresAt, "Listing expired"); require(msg.value >= listing.price, "Insufficient payment"); delete listings[tokenId]; (address royaltyReceiver, uint256 royaltyAmount) = royaltyInfo(tokenId, listing.price); uint256 sellerProceeds = listing.price - royaltyAmount - (listing.price * PLATFORM_FEE / 10000); payable(royaltyReceiver).transfer(royaltyAmount); payable(PLATFORM_TREASURY).transfer(listing.price * PLATFORM_FEE / 10000); payable(listing.seller).transfer(sellerProceeds); _safeTransfer(listing.seller, msg.sender, tokenId, ""); } Що входить в роботу над проєктом?
- Аудит та оптимізація смарт-контрактів (економія на gas до 30%).
- Повна документація та тести (Foundry/Hardhat).
- Інтеграція з The Graph або кастомним індексатором.
- Передача доступу до контрактів, IPFS та CDN.
- Навчання команди замовника роботі з системою.
- Технічна підтримка на етапі запуску.
Рендеринг карти: 2D vs 3D
| Підхід | Технологія | Продуктивність | Складність |
|---|---|---|---|
| 2D top-down | React + Pixi.js/Konva.js | Висока (viewport culling) | Середня |
| 3D world | Three.js/Babylon.js | Середня (LOD, streaming) | Висока |
Для першої версії достатньо 2D карти. 3D рендеринг — окрема ітерація, що потребує CDN для сцен та системи LOD.
Індексування даних
On-chain дані неефективно читати безпосередньо. Використовуйте індексатор: The Graph або кастомний з PostGIS.
type Land @entity { id: ID! x: Int! y: Int! owner: Bytes! districtId: Int content: LandContent listings: [Listing!]! @derivedFrom(field: "land") transactions: [Transfer!]! @derivedFrom(field: "land") } Стек технологій
| Компонент | Технологія |
|---|---|
| Land NFT контракт | ERC-721 + Solidity |
| Estate контракт | ERC-721 + adjacency logic |
| Marketplace | Solidity (кастомний або Seaport) |
| Індексатор | The Graph / кастомний + PostGIS |
| 2D карта | React + Pixi.js / Konva.js |
| 3D рендеринг | Three.js / Babylon.js |
| Content storage | IPFS + Pinata / Arweave |
| Мережа | Polygon / Immutable zkEVM |
Терміни та бюджет
MVP (Land NFT, базовий marketplace, 2D карта, завантаження контенту) — 2–3 місяці. Повна система з Estate, Districts, 3D рендерингом, системою прав, індексатором — 5–7 місяців. 3D world engine — окремий проєкт, 6–12 місяців. Кожен проєкт оцінюється індивідуально — отримайте консультацію. Гарантуємо безпеку контрактів (аудит обов'язковий) та досвід у подібних проєктах. Команда має 7+ років досвіду та 30+ завершених NFT-проєктів — оцінимо ваш проєкт за 1 день. Зв'яжіться з нами, щоб обговорити деталі.







