---
title: "Solana NFT Gates"
description: "Gate NFT Metaplex Core, метадані колекції KEYS, валюта продажу RING, стейкінг GateEscrow і торгові лістинги vendor gate"
locale: "uk"
---
# Solana NFT Gates

> **Info**
> Використовуйте вкладки **Founder** / **Developer** на бічній панелі документації, щоб відфільтрувати цю сторінку. Frontmatter `audience` керує блоками всередині сторінки; видимість у sidebar курується в `lib/docs/audience-curated-docs.ts`.

MVP-A постачає gate NFT **Solana Metaplex Core** для розблокування членства й vendor-функцій. Покупці сплачують первинний продаж нативним токеном клону (RING у цьому дереві), стейкають у **GateEscrow** (а не в `NATIVE_NFT_APR`), а `GateResolver.hasFeature` відкриває продуктові поверхні. Торгові vendor gates тепер можна виставляти на NFT Exhibition Marketplace, але придбаний gate усе одно треба **застейкати**, перш ніж функції активуються.

| Питання | Фактична реалізація |
|---------|--------|
| SSOT mint | Metaplex Core (`createCollection` / `create` / `fetchAsset`) |
| SSOT stake | GateEscrow DB + необов’язковий program id — **не** APR Ring staking |
| Первинний продаж | RING `transferChecked`, потім mint (`features/nft-gates/purchase.ts`) |
| Поверхня користувача | `/nft/gates` |
| Вторинна поверхня | `/nft/market`, `/nft/market/[listingId]`, `/nft/market/sell` |
| Admin | `/admin/nft/templates`, `/admin/nft/mint` |
| Провайдер підписки | `nft_gate` → `nftGateSubscriptionProvider` |
| Схема | `data/migrations/029_nft_gates_schema.sql` |
| Схема маркетплейсу | `data/migrations/031_nft_exhibition_market_schema.sql` |

### For founders

## Чому це важливо для вашого клону

Продавайте доступ як on-chain сертифікат, а не лише через картки чи кредитні rails. Gate членства є **soulbound**; vendor deed / DAGI / license gates **торгові** в NFT Exhibition Marketplace. Зміна ціни створює новий активний template asset — продані assets ніколи не змінюються.

### Шлях оператора

1. Один раз створіть **колекцію** Metaplex Core (admin mint console).
2. Задайте `nft.collectionMint` у `ring-config.json` (приклад devnet уже налаштований у цьому дереві).
3. Відкрийте **Mint / activate + art** → виберіть один із чотирьох preview ImageConductor → виконайте mint у колекцію.
4. Учасники купують на `/nft/gates`; membership slugs автоматично стейкаються й відкривають функції учасника.
5. Торгові vendor gates можна виставити на `/nft/market/sell`, лише якщо вони належать власнику, перевірені, не soulbound і не застейкані.
6. Перед mainnet: передайте **update authority** колекції та authorities маркетплейсу до Squads (див. `solana/SECURITY.md`).

### Symbol в Explorer: KEYS (не RING)

**Symbol** у Solana Explorer надходить із JSON off-chain metadata колекції за `uri` колекції — Metaplex Core не має on-chain symbol. Ring Platform використовує **`KEYS`** для сімейства gate NFT (`Ringdom Keys Collection`). **RING** залишається лише SPL **валютою продажу**.

Live family JSON (on-chain uri devnet): gist `collection.json` з `"symbol":"KEYS"`. SSOT застосунку: `public/nft/gates/collection.json`. Після деплою стабільний шлях `/nft/gates/collection.json` переписується на `/api/nft/gates/collection`.

### Типові сценарії

- Довічний membership NFT як постійна печатка учасника (потрібен stake).
- Vendor DAGI key застейканий до активації агента (`hasFeature('vendor.dagi')`).
- Store deed / DAGI / annual / quarterly vendor gates виставляються на перепродаж за RING, коли не застейкані.
- Придбаний vendor gate стейкається після покупки, перш ніж активується доступ до продукту.

  
- **[SubscriptionConductor](/docs/features/subscriptions.md)** — Як `nft_gate` працює поруч із credit, WayForPay, Stripe і native token.

  
- **[Wallet](/docs/features/wallet.md)** — Кастодіальні Solana-гаманці та перекази RING, які фінансують первинний продаж.

  
- **[RingFileBase](/docs/integrations/ring-filebase.md)** — Куди потрапляє згенероване ImageConductor NFT art.

  
- **[NFT Marketplace](/docs/features/nft-market.md)** — Запущений Exhibition Marketplace для допущених vendor KEYS gates; перевірений on-chain GateMarket залишається майбутньою роботою.

> **Warning**
> Покупка на marketplace передає лише право власності. Вона **не** відкриває функції, доки покупець не застейкає gate. Активний listing означає `hasFeature:false`; активний stake означає `canList:false`.

### For developers

## Архітектура

```mermaid
sequenceDiagram
  participant User
  participant Action as nft-gates actions
  participant Pay as purchaseGateNft
  participant Core as Metaplex Core
  participant Escrow as GateEscrow
  participant Resolve as GateResolver
  participant Market as NFT Market

  User->>Action: purchaseGateAction(slug)
  Action->>Pay: transferChecked RING
  Pay->>Core: mintGateAsset
  Core-->>Pay: asset + signature
  Pay->>Escrow: auto-stake membership slugs
  User->>Resolve: hasFeature(feature)
  Resolve->>Resolve: cache + stakes + fetchAsset
  User->>Market: list unstaked tradeable vendor gate
```

### Карта модулів

| Шлях | Роль |
|------|------|
| `features/nft-gates/umi-client.ts` | Sponsor umi з `SOLANA_FEE_PAYER_PRIVATE_KEY` |
| `features/nft-gates/metaplex-core-onchain.ts` | `createCollection`, mint, `fetchAsset`, soulbound freeze |
| `features/nft-gates/metaplex-core.ts` | Адаптер on-chain проти ledger-dev |
| `features/nft-gates/purchase.ts` | RING pay → mint → ownership |
| `features/nft-gates/gate-escrow.ts` | Stake / unstake + інвалідація entitlement |
| `features/nft-gates/gate-resolver.ts` | `hasFeature` (cache → stakes → RPC) |
| `features/nft-gates/admin-mint.ts` | Створення collection + активація template |
| `features/nft-gates/art-prompt.ts` | Prompt vars + favicon data URI |
| `features/nft-market/listing-policy.ts` | Tradeable SKU, soulbound, stake, перевірки duplicate listing |
| `features/nft-market/services/listing-service.ts` | Життєвий цикл NFT Exhibition listing |
| `features/nft-market/services/solana-market-client.ts` | Адаптер marketplace ledger-dev; Anchor branch заблокований |
| `features/admin/nft/gate-art-preview-modal.tsx` | fs-modal із preview 4-up |
| `app/_actions/nft-gates.ts` | Server actions |
| `app/_actions/nft-market.ts` | List, cancel, purchase marketplace actions |
| `lib/payments/subscription/providers/nft-gate-subscription.ts` | Провайдер `nft_gate` |

### Конфігурація (`ring-config.json` → `nft`)

Перевірені ключі: `enabled`, `collectionMint`, `collectionUri`, `collectionSymbol` (`KEYS`), `sponsorFeePayer`, `marketplaceEnabled`, `marketplaceFeeRecipient`, `marketplaceFeeBps`, `gateEscrowProgramId`, `gateMarketProgramId`, `templates[]`.

Off-chain family metadata: `public/nft/gates/collection.json` (`symbol: KEYS`). Admin **Point collection URI → KEYS JSON** запускає Metaplex Core `updateCollection`, доки sponsor все ще має update authority.

Сім template-ів (slugs): `one-month-membership`, `annual-membership`, `lifetime-membership`, `vendor-store-deed`, `vendor-dagi-key`, `vendor-annual-store-license`, `vendor-quarterly-store-license`.

`payment.gateways.nft_gate.enabled` у цьому дереві має значення **true**. `nft_gate` залишається в `payment.futureMethods` (ще не в `supportedMethods`), тому membership PaymentModal може й надалі позначати його як coming-soon, хоча `/nft/gates` і модуль provider уже працюють.

Торгові marketplace slugs навмисно вужчі: `vendor-store-deed`, `vendor-dagi-key`, `vendor-annual-store-license` і `vendor-quarterly-store-license`. Membership slugs залишаються soulbound і не можуть бути виставлені.

### Server actions

| Action | Призначення |
|--------|---------|
| `purchaseGateAction` | Купити gate NFT за RING |
| `stakeGateAction` / `unstakeGateAction` | GateEscrow |
| `hasGateFeatureAction` | Перевірка entitlement |
| `previewGateArtAction` | ImageConductor `n: 4` + посилання на favicon |
| `adminActivateGateTemplateAction` | Mint/activate з необов’язковим `imageUri` |
| `adminCreateGateCollectionAction` | Створити Core collection |

### Потік preview art адміністратора

Відкрийте `/admin/nft/templates` або `/admin/nft/mint` і натисніть **Mint / activate + art**.

`previewGateArtAction` підставляє `$projectName`, `$activeColor`, `$secondaryColor`, `$projectColor1`, `$projectColor2`, `$organization`, потім викликає `ImageConductor.generate` з `n: 4` і favicon `referenceImages`.

Fullscreen modal показує сітку 2×2, згортане поле **Generation prompt** і **Regenerate** (зміни prompt лише на час сесії — у ring-config не записуються).

**Mint with selected art** викликає `adminActivateGateTemplateAction({ imageUri, regenerateArt: false })` → Metaplex Core mint → рядок edition `nft_gates` + **db() active-template pointer** (`nft_active_{slug}`). Runtime SSOT — `listNftGateTemplatesResolved()` (дефолти ring-config + overlay DB). `ring-config.json` у k8s ніколи не переписується.

### Підключення image і storage

| Частина | Шлях / env |
|-------|------------|
| Conductor | `lib/images/conductor/image-conductor.ts` |
| xAI provider | `lib/images/providers/xai.provider.ts` — `/images/generations` або `/images/edits` |
| Favicon PNG | `public/images/favicon.png` → `data:image/png;base64,…` |
| Persist | `file().upload` → `RingBaseAdapter`, коли `NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase` |
| Local→prod API | Port-forward `ring-filebase-api` до `127.0.0.1:18080` (див. [RingFileBase](/docs/integrations/ring-filebase.md)) |

{`SOLANA_RPC_URL=https://api.devnet.solana.com
SOLANA_FEE_PAYER_PRIVATE_KEY=
XAI_API_KEY=
IMAGE_GEN_PROVIDER=xai
NEXT_PUBLIC_STORAGE_PROVIDER=ring_filebase
RINGBASE_API_URL=http://127.0.0.1:18080
NEXT_PUBLIC_RINGBASE_API_URL=http://127.0.0.1:18080
RINGBASE_PUBLIC_URL=https://cdn.ring-platform.org
RINGBASE_API_TOKEN=
# Optional ledger-dev mint without collectionMint:
# ALLOW_LEDGER_NFT_MINT=1`}

> **Warning**
> **Не** задавайте `RINGBASE_API_URL=https://api.ring-platform.org` — цей host є ingress Next.js app, а не `ring-filebase-api`. Upload повертає HTML і проявляється як HTTP 500 у `RingBaseAdapter`.

### Колекції persistence

Міграція `029_nft_gates_schema.sql` створює JSONB collections: `nft_gates`, `nft_stakes`, `nft_entitlement_cache`, `nft_ownership` (і пов’язані індекси). Cron: `GET /api/cron/nft-gate-expiry` → `runNftGateExpiry` (щоденний розклад о 4am описано в документації SubscriptionConductor).

Міграція `031_nft_exhibition_market_schema.sql` додає persistence вторинного ринку: `nft_listings`, `nft_market_sales` і `nft_market_collections`.

### Ledger-dev проти on-chain

Якщо `nft.collectionMint` порожній, `metaplex-core.ts` видає asset id ledger-dev не в production або коли `ALLOW_LEDGER_NFT_MINT=1`. Production відмовляється від ledger mint без цього flag.

Якщо `nft.gateMarketProgramId` порожній, `SolanaMarketClient` використовує поставлений ledger-dev market adapter. Якщо в цій збірці задано program id, list/cancel/buy викидають помилку, оскільки перевірений Anchor GateMarket CPI ще не реалізований.

Anchor scaffolds знаходяться в `solana/programs/{gate-escrow,gate-market}`. `solana/SECURITY.md` не допускає обидві програми до production, доки не завершені аудит, контроль authorities через Squads, pinned IDs/IDLs і тести інваріантів.

## Беклог (не поставлено)

## Пов’язана документація

  
- **[SubscriptionConductor](/docs/features/subscriptions.md)** — Реєстр провайдерів, що містить `nft_gate`.

  
- **[Generative images](/docs/development/generative-images.md)** — Провайдери ImageConductor і persistence у ring-filebase.

  
- **[RingFileBase](/docs/integrations/ring-filebase.md)** — Upload API проти CDN read path.

  
- **[Staking](/docs/features/staking.md)** — APR pools — не плутайте з GateEscrow.

  
- **[NFT Exhibition Marketplace](/docs/features/nft-market.md)** — Вторинна торгівля допущеними vendor KEYS gates.
