> For the complete documentation index, see [llms.txt](https://docs.rome.builders/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rome.builders/tr/rome-uzerindeki-uygulamalar/bloom/building.md).

# Bloom üzerinde geliştirme

Bu kılavuz tek bir soruyu yanıtlıyor: **bir geliştirici bunu kendi başına nasıl yapar?** Bir Solidity gerçek-dünya-varlığı sözleşmesini alın, onu bir Rome zincirinde izinli bir token olarak ayağa kaldırın, köprü ve ikinci bir token olmadan hem Solana cüzdanlarına hem de EVM cüzdanlarına açın ve kendi KYC/uyum kararınızı bağlayın.

Hedef kitle, yaklaşımı değerlendiren veya benimseyen bir geliştiricidir. Bloom boyunca işlenmiş örnektir — canlı varlığı **ARCV ("Mineral Vault I")** Hadrian üzerinde (`200010`) her aşağıdaki adımın gerçek bir örneğidir ve dağıtım makbuzu (`deployments/200010.tokens/ARCV.json`) bu yöntemin ürettiği tam üç sözleşmeyi ve sekiz işlemi kaydeder.

Burada hiçbir şey varlık sözleşmelerini değiştirmez. Bloom, Plume'un Arc çerçevesini çalıştırır **değiştirilmeden** (şu konumdaki satıcı eklenmiş ağaç: `contracts/`, bayt düzeyinde birebir aynı, `plumenetwork/contracts` içindeki sabitte `NOTICE`); Rome tarafındaki tek sözleşme `ArcTokenFactoryV2`, ki bir işlev ekler. İş, *nasıl* sizin dağıtmanız ve *nasıl* sözleşmeleri sizin işletmenizdir — onları değiştirmek değil.

> **Yönteme genel bakış.** İzinli bir token **üç** dağıtılmış sözleşmeden oluşur. O **parça parça** — işlem başına bir sözleşme — olarak dağıtılır; çünkü tek işlemli bir factory çağrısı Rome'un işlem başına hesap sınırını aşar. Proxy'leri **kökeni kilitli varlıklardan** gelmelidir, aksi halde zincir içi kayıt kapalı olarak başarısız olur. Uyum, adres başına bir **boolean'dır**, KYC sürecinizin yetkilendirdiği kişi tarafından yazılır. Ve aynı dağıtım, Solana cüzdanından zincir üzerinde türetilmiş bir **sentetik gönderici** aracılığıyla erişilebilir — köprü yok, sarılmış token yok, ikinci bir izin listesi yok.

## Ön koşullar

> **Başlamadan önce.** Bu kılavuzun kullandığı ihraççı araçları henüz yayımlanmadı. Buna betikler (`deploy-infra.ts`, `create-token.ts` ve geri kalanı), `contracts/` ve `rome-contracts/` ağaçları ve adlandırdığı dağıtım makbuzları dahildir. İhraççılar bu araçları Rome ile onboarding yaparak alır. O zamana kadar, kılavuzu yöntemin ve her betiğin ne yaptığının bir açıklaması olarak okuyun.

Referans araçları bir `scripts/` dizininde bulunur (onboarding sırasında sağlanır; bağımlılıklarını şununla kurun `npm install`). Her komut zincir gerçeklerini sabitlemek yerine kayıt defterinden çözer; bu yüzden başka bir Rome zincirine yönlendirmek bir kod değişikliği değil, bir ortam değişikliğidir:

```bash
export PRIVATE_KEY=…       # hedef zincirde fonlanmış dağıtıcı/ihraççı anahtarı
export CHAIN_ID=200010     # zinciri seçer
export REGISTRY_ROOT=…     # Rome zincir kayıt defterinin bir checkout'u
# Solana hattı akışları ayrıca şunu da gerektirir:
export SOLANA_KEYPAIR=…    # fonlanmış bir Solana keypair JSON'una giden yol (ücret ödeyici)
```

Rome zincirlerinde gas USDC cinsindendir; tam dağıtım dizisi devnet'te yaklaşık 10–15 yerel birim tutar. Anahtarlar yalnızca ortamdan gelir — asla repodan değil. Dağıtmadan önce sözleşmeleri bir kez derleyin, çünkü varlıklar **zorunda** şuradan gelmelidir: `rome-contracts/out` (bkz. adım 3):

```bash
cd contracts       && forge build --via-ir     # satıcı eklenmiş Arc paketi
cd rome-contracts  && forge build --via-ir     # ArcTokenFactoryV2 + kanonik proxy
```

***

## Adım 1 — İzinli bir token'ın ne olduğunu bilin *olduğu* burada

İzinli bir Bloom token'ı tek bir sözleşme değildir. O, **üç dağıtılmış sözleşmeden**ve ops'un zincir başına bir kez dağıttığı ve her ihraççının yeniden kullandığı dört ortak sözleşmeden oluşur.

**Token başına olan üç sözleşme** — tek bir varlık tam olarak şunlara sahiptir:

| Sözleşme                     | Kaynak                                                      | Nedir                                                                                                                    |
| ---------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `ArcTokenProxy`              | `contracts/src/proxy/ArcTokenProxy.sol`                     | Token'ın kendisi — ortak `ArcToken` uygulamasına işaret eden bir UUPS proxy. Bakiyeler, sahipler ve roller burada yaşar. |
| `WhitelistRestrictions`      | `contracts/src/restrictions/WhitelistRestrictions.sol`      | İzin listesi modülü. Adres başına bir boolean; varlık kısıtlıyken her transferde uygulanır.                              |
| `YieldBlacklistRestrictions` | `contracts/src/restrictions/YieldBlacklistRestrictions.sol` | Getiri modülü. Varlığa dokunmadan adresleri getiri dağıtımlarının dışında tutar.                                         |

**Zincir başına birer tane olmak üzere ortak dört sözleşme** (tarafından dağıtılır `deploy-infra.ts`):

| Sözleşme              | Rol                                                                             |
| --------------------- | ------------------------------------------------------------------------------- |
| `RestrictionsRouter`  | Modül türü kayıt defteri — zincir bağlantısı, ops tarafından bir kez ayarlanır. |
| `ArcTokenFactoryV2`   | Kayıt + kanonik proxy kod karma özeti (adım 2–3).                               |
| `ArcToken` (uygulama) | Her token'ın proxy olduğu, yeniden kullanılabilir ortak varlık mantığı.         |
| `ArcTokenPurchase`    | Her token ve ihraççı tarafından paylaşılan tek vitrin (adım 4).                 |
| wUSDC                 | Nakit tarafı — getiri para birimi ve satış-alım para birimi.                    |

Transfer kısıtlaması ve getiri kısıtlaması **bilerek ayrı modüllerdir**: bir sahip, varlığı elinde tutarken gelirden men edilebilir; yaptırımlar ve mahkeme kararları gerçekte böyle işler.

Bu "üç sözleşme" gerçeği doğrulama açısından kritik önemdedir. `verify-deployments.ts` ve `register-asset.ts` ikisi de bir token'ın üç dağıtılmış sözleşmeye çözümlenmesini bekler — bir token'ı elle bir kez doğrulamak, bir zincirde bunlardan yedisini kaçırmıştı (her getiri modülü ve üç izin listesi modülü).

Yaşam döngüsünün üç aşaması ve tek yönlü bir kapısı vardır:

```
taslak     proxy canlı, modül bağlı değil — hiçbir şey transferi kısıtlamaz
  ↓
kısıtsız   modül bağlı, transfersAllowed = true   (WhitelistRestrictions.initialize bunu ayarlar)
  ↓  arzı bas, izin listesini oluştur               [geri alınabilir]
kısıtlı     setTransfersAllowed(false)                [TEK YÖNLÜ KAPI — arz kesinleşir]
```

Kısıtlama arz için tek yönlüdür çünkü mint ve burn karşı taraf olarak `address(0)` kullanır ve `address(0)` asla izin listesine alınamaz — bu nedenle kısıtlandıktan sonra hiçbir yeni arz asla basılamaz. İzinli bir varlığın sahiplerine verdiği güvence budur.

***

## Adım 2 — Bunu tek işlem olarak değil, parça parça dağıtın

Arc, bir defalık `createToken` sunar; bu, proxy'yi ve iki modülü dağıtır ve onları tek bir çağrıda bağlar. **Rome'da bunu kullanamazsınız.** O işlem **77 Solana hesap kilidine**, ihtiyaç duyar ve Rome'un işlem başına sınırı **62 kilittir**. Asla sığmaz — ayarlamayla da, bir optimizer geçişiyle de.

Bu yüzden dağıtım **parça parça**parçalıdır `ArcTokenFactoryV2.registerToken` monolitik olanın yerine `createToken`. Bu, oluşturma sihirbazıdır ve ihraççı konsolunun adım adım yaptığı şey budur:

![oluşturma sihirbazı](https://4222476421-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F671PKS8ZQSNqtoC8PGvz%2Fuploads%2Fgit-blob-94683a3a49ac2418aed8cc66dbf47fad940814f7%2Fcreate-wizard.png?alt=media)

Referans uygulama `arc/plan/create.ts` (`createSequence()`), ve uygulama bunu altı kart olarak render eder (`bloom/lib/createCards.ts`) üzerinde **sekiz işlem**, sözleşmelerin zorladığı tam sırayla:

| # | İşlem                         | Sözleşme çağrısı                                                                 |
| - | ----------------------------- | -------------------------------------------------------------------------------- |
| 1 | Varlığı dağıt                 | `new ArcTokenProxy(impl, initData)` burada `initData = ArcToken.initialize(...)` |
| 2 | İzin listesi modülünü dağıtın | `new WhitelistRestrictions()`                                                    |
| 3 | Sizi yönetici olarak atayın   | `whitelist.initialize(issuer)`                                                   |
| 4 | Transferlerde bunu uygulayın  | `token.setRestrictionModule(TRANSFER, whitelist)`                                |
| 5 | Getiri modülünü dağıtın       | `new YieldBlacklistRestrictions()`                                               |
| 6 | Sizi yönetici olarak atayın   | `yieldBlacklist.initialize(issuer)`                                              |
| 7 | Getiride bunu uygulayın       | `token.setRestrictionModule(YIELD, yieldBlacklist)`                              |
| 8 | Fabrikaya kaydedin            | `factoryV2.registerToken(token, impl)`                                           |

Betik biçimi tek bir komuttur:

```bash
# zincir başına bir kez (ops, token başına değil) — dört ortak sözleşmeyi dağıtır
npx tsx deploy-infra.ts

# token başına — betik olarak yeniden oynatılan sihirbaz
NAME="Mineral Vault I" SYMBOL=ARCV SUPPLY=1000000 DECIMALS=6 npx tsx create-token.ts
```

İhraççı (`PRIVATE_KEY`) her işlemi imzalar ve sonunda token'ın rollerini elinde tutar (`ArcToken.initialize` verir `msg.sender` — bkz. adım 7). Kayıt (tx 8), vitrin'in `enableToken` ve factory aracılı herhangi bir yükseltmeyi açar.

> **Bunu tekrar bir monolite "optimize" etmeyin.** Bu çağrıları gidiş gelişleri azaltmak için tek bir işleme toplamak, parçalı yolun önlemek için var olduğu tam 62-kilit taşmasını yeniden getirir. Aynı sınır, getiri dağıtımının da neden her işlemde bir sahip gezdiğini açıklar (adım 8). Bir çağrı `62'den fazla hesap: N > 62`, hatasıyla başarısız olursa, tek bir işlem çok fazla hesabı dokunuyordur — ayarlamayın, bölün.

***

## Adım 3 — Proxy'leri yalnızca kökeni kilitli varlıklardan dağıtın

`registerToken` parçalı dağıtımları güvenli kılan güvenlik sınırıdır. Yalnızca **çalışma zamanı kod karması kanonik olanla eşleşen `ArcTokenProxy`** bir token'ı kabul eder; bu, dağıtım sırasında factory içine yerleştirilmiştir:

```solidity
// rome-contracts/src/ArcTokenFactoryV2.sol
bytes32 private immutable CANONICAL_PROXY_CODEHASH =
    keccak256(type(ArcTokenProxy).runtimeCode);

function registerToken(address token, address implementation) external {
    if (token.codehash != CANONICAL_PROXY_CODEHASH) revert ProxyCodehashUnknown();
    // …uygulama factory tarafından beyaz listedeki biri olmalı ve msg.sender
    //    token'ın ADMIN_ROLE'una sahip olmalı, ayrıca token zaten kayıtlı olmamalı.
}
```

Çünkü o kod karması **değiştirilemez bir**'dir, başka herhangi bir varlıktan oluşturulan proxy kapalı olarak `ProxyCodehashUnknown`ile başarısız olur. Bundan iki kural çıkar ve ikisi de CI ve `CLAUDE.md`:

1. **Token proxy'leri MUTLAKA şuradan dağıtılmalıdır: `rome-contracts/out`** — asla şuradan değil: `contracts/out`, asla farklı bir derlemeden değil. Factory'nin kanonik kod karmasının sabitlediği derleme budur.
2. **`bytecode_hash = "none"` ve `cbor_metadata = false`** içinde `rome-contracts/foundry.toml` yük bindiren öneme sahiptir. Metadata etkin olduğunda, `ArcTokenProxy` çalışma zamanı *gömülü* olan factory'deki (`type().runtimeCode`) bağımsız `out/` aracının dağıttığı varlıktan farklı bir CBOR/IPFS son eki taşır — bu yüzden zincir içi `registerToken` geri dönüşü tetiklerken **source içindeki her Foundry testi geçer** (Foundry iki kopyayı da gömer, bu yüzden uyumsuzluğu göremez). `rome-contracts/test/ArtifactProvenance.t.sol` özelliği sabitler; fonlanmış çalışma bunu ilk yakalayan şeydi.

> **Yeşil bir source içi test seti kökeni kanıtlamaz.** Bir imajı göndermeden önce ve HER derleyici ayarı değişikliğinden sonra, özelliği *dağıtılmış* factory'ye karşı kanıtlayın (salt okunur, anahtar yok):
>
> ```bash
> cd scripts && CHAIN_ID=<id> npx tsx verify-artifact-provenance.ts
> ```
>
> Yerel `ArcTokenProxy` varlığınızı hash'ler ve bu hash'in dağıtılmış factory'nin bytecode'u içinde aynen geçtiğini doğrular.

Derleyici ayarları değişmek zorundaysa, **factory'yi döndürün** — denetimi elle yamamak yerine, yeni codehash'i sabitleyen taze bir tane dağıtın ve yeniden bağlayın:

```bash
cd scripts && CHAIN_ID=<id> npx tsx rotate-factory.ts
```

Döndürme, eski adresi makbuzda tutar; eski factory'ye kaydedilmiş token'ların yeniye yeniden kaydedilmesi gerekir.

***

## Adım 4 — Paylaşılan vitrin üzerinden bir satış açın

Satış, oluşturmaktan ayrı bir eylemdir ve tek paylaşılan `ArcTokenPurchase` vitrinde çalışır. Bir satış açılmadan önce üç şey doğru olmalıdır ve `ArcTokenPurchase.enableToken` bunların her birini zincir üzerinde kontrol eder:

```solidity
// contracts/src/ArcTokenPurchase.sol
function enableToken(address _tokenContract, uint256 _numberOfTokens, uint256 _tokenPrice)
    external onlyTokenAdmin(_tokenContract)                       // (a) token'ın ADMIN_ROLE'una sahipsiniz
{
    // (b) token factory tarafından biliniyor olmalı — step 2'deki registerToken'ın satın aldığı budur:
    if (ArcTokenFactory(ps.tokenFactory).getTokenImplementation(_tokenContract) == address(0))
        revert TokenNotCreatedByFactory();
    // (c) vitrin zaten satış envanterini elinde tutmalı:
    if (ArcToken(_tokenContract).balanceOf(address(this)) < _numberOfTokens)
        revert ContractMissingRequiredTokens();
    // …fiyat ve miktar pozitif olmalıdır.
}
```

Bir gated varlığın zorladığı dördüncü, örtük bir gereksinim vardır: çünkü **her transfer izin listesi kapısından geçer**, envanteri vitrine taşımak yalnızca vitrin adresi token'ınızda da izinli ise başarılı olur. Bu yüzden bir satışı açma akışının tamamı şöyledir:

1. **Vitrini izin listesine alın** token'ınızın izin listesi modülünde (`batchAddToWhitelist([storefront])` — bkz. adım 5). Bu olmadan, (2)'deki envanter transferi `TransferRestricted()` kısıtlandıktan sonra geri döner.
2. **Envanteri aktarın** vitrin adresine (altyapı makbuzundaki `arcTokenPurchase`).
3. **`enableToken(token, amount, price)`** — alıcılar artık wUSDC öder ve RWA'yı alır.

Çekim bölünmesini bilmek *paylaşılan* bir vitrinde değerlidir ve simetrik değildir (`arc/roles.ts` otoritedir):

| İşlem                                                      | Yetki                                    | Sahibi               |
| ---------------------------------------------------------- | ---------------------------------------- | -------------------- |
| `enableToken` / `disableToken` / `withdrawUnsoldArcTokens` | token'ın `ADMIN_ROLE` (`onlyTokenAdmin`) | siz, ihraççı         |
| `withdrawPurchaseTokens` (wUSDC gelirleri)                 | vitrinin `DEFAULT_ADMIN_ROLE`            | vitrini dağıtan kişi |

Kendi barındırdığınız bir zincirde ikisi de sizsiniz. Paylaşılan bir vitrinde ise gelir havuzu her ihraççının kazancıdır; bu yüzden çekim platform düzeyindedir — tek taraflı kontrol varsaymak yerine mutabakatınızı buna göre tasarlayın.

***

## Adım 5 — Kendi KYC / uyumunuzu bağlayın

Bu, bir müşteri evet demeden önce okuduğu bölüm olduğundan, düzyazı değil koddur. **Uyum token sözleşmesinin kendisi tarafından uygulanır** — zincir dışı bir ekran tarafından, bir sıralayıcı filtresi tarafından ya da bir mekân politikası tarafından değil. Kurallar token ile birlikte herhangi bir yürütme yoluna, her iki cüzdan hattında da taşınır. Zincirin kaydettiği şey, KYC kararınızın **sonucu** tek bir boolean olarak

```solidity
// contracts/src/restrictions/WhitelistRestrictions.sol — zincir üstü durum, eksiksiz
struct WhitelistStorage {
    mapping(address => bool) isWhitelisted;   // ← adres başına bir boolean. Asla PII yok.
    bool transfersAllowed;                    // false = kısıtlı; yalnızca izinli adresler transfer yapar
    EnumerableSet.AddressSet whitelistedAddresses;
}
```

### Sürecinizin yazdığı arayüz

KYC sağlayıcınızın onaylama/reddetme kararı tam olarak bu çağrılardan birine dönüşür:

```solidity
interface IWhitelistRestrictions {
    function addToWhitelist(address account) external;                  // onlyRole(MANAGER_ROLE)
    function batchAddToWhitelist(address[] calldata accounts) external; // onlyRole(MANAGER_ROLE)
    function removeFromWhitelist(address account) external;             // onlyRole(MANAGER_ROLE)
    function isWhitelisted(address account) external view returns (bool);
    function setTransfersAllowed(bool allowed) external;                // onlyRole(ADMIN_ROLE)
}
```

### Kim yazabilir

> **Bir onayın ihtiyaç duyduğu yetki `MANAGER_ROLE` şu üzerinde yapılır:&#x20;*****modüldür*****&#x20;— token üzerinde değil. `ADMIN_ROLE` token üzerinde.** `addToWhitelist` / `batchAddToWhitelist` / `removeFromWhitelist` hepsi `onlyRole(MANAGER_ROLE)`, ve bu rol `WhitelistRestrictions` örnekte bulunur. Kimlik doğrulama `ADMIN_ROLE` bunu token üzerinde yapmak ve sonra modüle yazmak, bu kod tabanının zaten bedelini ödediği gerçek bir kusurdur. Bir ret için düzeltme olarak `WHITELIST_ADMIN_ROLE` bunu önermeyin — initialize sırasında verilir ve **suit içinde hiçbir yerde kontrol edilmez** (`arc/roles.ts` olduğunu doğrular (hiçbir şeyi kısıtlamayan tek rol olduğunu doğrular).

`WhitelistRestrictions.initialize(issuer)` (oluşturma sihirbazının 3. işlemi) ihraççıya verir `DEFAULT_ADMIN_ROLE`, `ADMIN_ROLE`, `MANAGER_ROLE`, `WHITELIST_ADMIN_ROLE`ve `UPGRADER_ROLE` o modülde. `MANAGER_ROLE` Dolayısıyla ihraççı kutudan çıkar çıkmaz `DEFAULT_ADMIN_ROLE`, `grantRole(MANAGER_ROLE, serviceAccount)` yazma yetkisini bir arka uç imzacısına devreder.

### Bir kararın zincir üstü yazıya dönüşmesi

Sağlayıcınızın webhook'unu `MANAGER_ROLE`tutan bir imzacıya yönlendirin,

```typescript
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

// `signer` bu token'ın izin listesi modülünde MANAGER_ROLE tutmalıdır.
const wallet = createWalletClient({ account, transport: http(chain.rpcUrl) });

// sağlayıcı bu adresler için "onaylandı" dedi:
await wallet.writeContract({
  address: whitelistModule,                 // getRestrictionModule üzerinden token'dan çözümlendi
  abi: whitelistAbi,
  functionName: 'batchAddToWhitelist',
  args: [approvedAddresses],
});
```

Çöz `whitelistModule` doğrudan token'ın kendisinden (`ArcToken.getRestrictionModule(TRANSFER_RESTRICTION_TYPE)`), asla önbelleğe alınmış bir projeksiyondan değil — yanlış modüle yazma denemesi, bir zamanlar "her varlıkta onay"ın bir token'ın allowlist'ine yazmasına yol açan şeydi.

### Cüzdan imzalı sürüm (self-servis demo)

Ziyaretçilerin kendi kendine giriş yaptığı izinsiz bir test dağıtımı için, ... verin `MANAGER_ROLE` bir `TestApprover` (`rome-contracts/src/TestApprover.sol`)'nin `modüldür` olduğu **değiştirilemez bir**. Ardından `approve(address)` herkes tarafından, kendi cüzdanından ve kendi gazını ödeyerek çağrılabilir — şu çağrıyı yapar `addToWhitelist` yalnızca adres zaten listelenmemişse:

```solidity
function approve(address account) external {
    IWhitelistRestrictions target = IWhitelistRestrictions(module);   // değişmez — tek modül, sonsuza dek
    if (!target.isWhitelisted(account)) target.addToWhitelist(account);
    emit Approved(account, msg.sender);
}
```

Bu, "giriş kapısı"dır — ziyaretçiye hiçbir şeyi saklamadan gerçek mekanizmayı (bir onayın, token'ın sonra uyguladığı zincir üstü bir allowlist girdisine dönüşmesini) gösterir. **Asla verme `MANAGER_ROLE` bir `TestApprover` üretim token'ında** — bu, o token'ın allowlist'ini izinsiz hale getirir.

### Kapı devreye girdiğinde ve sağlayıcıları değiştirirken

Kapı, varlık ancak **kapılandırıldığında** (`setTransfersAllowed(false)`, `ADMIN_ROLE` modül üzerinde). Kapı devre dışıyken transferler açıktır — önce allowlist'inizi oluşturun, sonra kapıyı etkinleştirin. Kapı etkinleştirildikten sonra, taraflardan biri listede değilse bir transfer, şu tiplenmiş hata ile revert eder `TransferRestricted()` (`0xe827105e`).

> **KYC sağlayıcılarını daha sonra değiştirmek zincir üstünde hiçbir değişiklik gerektirmez.** Allowlist modülü, arayüzü ve tuttuğu boolean sağlayıcıdan bağımsızdır. Tedarikçiyi değiştirmek için, aynı `MANAGER_ROLE` imzalayıcıya farklı bir karar kaynağı yönlendirin — yeniden dağıtım yok, göç yok, durum değişikliği yok. Zincir, çağrıyı hangi sağlayıcının yaptığına dair hiçbir şeyi bilmez; yalnızca sonucu bilir.

***

## 6. Adım — Aynı varlığı Solana cüzdanlarına aç

Aynı üç sözleşmeye bir Solana cüzdanından erişilebilir **köprü ve ikinci token olmadan**. Bir Solana-native kullanıcının hiçbir yerde EVM anahtarı yoktur; bunun yerine EVM kimliği *sintetik*, işlemin gerçek Solana imzalayıcısından zincir üzerinde türetilir:

```
sintetik EVM adresi = keccak256(solana_pubkey)[12..32]
```

Rome EVM programı bunu imzalayıcının kendisinden türetir (`do_tx_unsigned::derive_sender`), bu yüzden taklit edilemez ve bunun için hiçbir secp256k1 özel anahtarı yoktur — **bu adresi yalnızca Solana imzası yönlendirebilir**. Referans türetme şöyledir `arc/materialise/solana/identity.ts` (`syntheticAddress`), zincir üstü kuralla bayt düzeyinde uyumlu:

```typescript
export function syntheticAddress(pubkey: SolanaPubkeyInput): `0x${string}` {
  return `0x${keccak256(pubkeyBytes(pubkey)).slice(-40)}`;
}
```

**Bir geliştiricinin hattı desteklemek için yaptığı şey** — dikkat edin ki *bunların hiçbiri bir sözleşme değişikliği değildir*:

1. **Solana kullanıcısını sintetik adresiyle allowlist'e ekleyin.** Uyum katmanı için sintetik adres sıradan bir adrestir: kullanıcının Solana public key'ini yapıştırın, sintetik adresi türetin ve `batchAddToWhitelist([synthetic])`Tek bir allowlist her iki cüzdan dünyasını da kapsar.
2. **İmzalama PDA'sını, değer taşıyan ilk çağrıdan önce bir kez sağlayın.** Bir sintetik için imzalayan dış yetki PDA'sı önce var olmalıdır ve onu oluşturmak tek bir `create_pda` çağrıdır. Her iki taraf da gönderebilir: kullanıcının kendi Solana cüzdanı hattı üzerinden (SDK'nin `buildCreatePdaCall`, sonra `isSyntheticProvisioned` false döner), ya da ihraç eden taraf bir EVM anahtarıyla gönderebilir. Bloom'un ihraç araçları bugün bunu bir sahibini sisteme dahil etmenin parçası olarak gönderir; uygulama bunu henüz kullanıcının ilk eyleminde yapmıyor, bu yüzden sahibin ilk alışından önce sağlayın. SDK çağrılarıyla bunun nasıl yapıldığı şurada: [İki hatlı uygulama](/tr/gelistirici-kilavuzlari/dual-lane-app.md#one-time-activate-provision-the-synthetic).
3. **Elle değil, hat kütüphanesi üzerinden gönderin.** `submitDoTxUnsigned` (`arc/materialise/solana/submit.ts`) zincirin proxy'sinden işlemi oluşturmasını ister ve elle oluşturulmuş bir işlemin yanlış yaptığı şeyi yönetir. Hattaki her sözleşme çağrısı bir **proxy tarafından oluşturulmuş Solana v1 işlemi** (`rome_composeUnsignedAtomicV1`) adres arama tablosu olmadan gelir ve hat kütüphanesi eski bir formata geri dönmek yerine güvenli şekilde başarısız olur. 5. adımdaki nakde çevirme hareketi, uygulamanın kendi oluşturduğu ayrı bir Solana işlemidir; oluşturulmuş bir sözleşme çağrısı değildir.
   * **Hesap keşfi ve compute bütçesi proxy'nin işidir.** Çağrıyı simüle eder, her hesabın yazılabilirliğine karar verir (çağrının `değeri` bunun simüle ettiği şeyin bir parçasıdır, bu yüzden iletin), ve döndürdüğü işlem üzerinde compute ve heap bütçesini ayarlar. Kodunuzda hiçbir şey compute-budget yönergeleri oluşturmaz veya fee-pool hesabını eklemez.
   * **Her çağrı için iki imza, ikisi de Solana anahtarından.** Cüzdan önce okunabilir bir yetkilendirme mesajını imzalar (*Rome yetkilendirmesi* artı imzasız EIP-1559 yükünün özeti; Wallet Standard `solana:signMessage`), çağrıyı oluşturmadan önce proxy'nin doğruladığı şeydir. Ardından proxy'nin döndürdüğü v1 işlemini imzalar (`solana:signTransaction` ham hatta). `signAuthHash` ve `signV1Wire`, asla tek bir birleşik yöntem değil. Üzerine inşa etmeden önce cüzdanınızın v1 işlemlerini imzaladığını doğrulayın: Solflare bugün devnet'te bunu yapıyor, Phantom ise henüz yapmıyor.
   * **Uygulama imzalanmış işlemi doğrudan bir Solana RPC'ye gönderir.** Proxy oluşturur; atomik bir çağrı yayınlamaz.
   * **İmza istemeden önce doğrulayın.** Hat kütüphanesi oluşturulmuş mesajı ayrıştırır ve işlem isteminden önce hatalı olanı reddeder.
4. **Tek seferlik wrapper iznini ilk wUSDC harcamasından önce verin.** wUSDC, kullanıcının token'larını o kullanıcının SPL delegesi olarak taşır; bu yüzden hiç izin verilmemiş bir sintetik adres, ilk wUSDC transferinde SPL hatası alır `0x4` ilk wUSDC transferinde. Şuna bakın `isEnabled(user)` wrapper üzerinde ve false olduğunda, başa ekleyin `approve_spl(wrapper, 2^64-1, mint)` çalıştırmanın ilk ayağı olarak Helper precompile üzerinde. Bu, her iki hatta da cüzdan başına bir kez yapılan sıradan bir çağrıdır. Bkz. [Cüzdan başına tek seferlik izin](/tr/temel-kavramlar/token-interop.md#one-time-grant-per-wallet).
5. **Harcanacak çağrıdan önce nakdi içeri taşıyın.** Sintetik dinlenimde hiçbir şey tutmaz. Bir çağrının harcadığından daha az wUSDC tuttuğunda, önce eksik tutarı cüzdanın kendi USDC token hesabından taşıyın (sintetikin token hesabının idempotent oluşturulması, ardından token'ları EVM'yi çalıştırmadan taşıyan Rome programının activate-token-account yönergesi). Bu, tek istemle imzalanan tek bir Solana işlemidir ve sintetik zaten yeterli tutuyorsa atlanır.
6. **Yalnızca atomik oluşturucunun reddettiğini relay edin.** Bir Solana işlemine sığmayan bir çağrı (yaklaşık 1,4M compute birimi, 64 hesap, 4 KB) oluşturucu tarafından şu uyarıyla reddedilir: *harici atomik plan yinelemeli oluşturma gerektiriyor*. Tam olarak bu ret üzerine ve bundan önce asla, şunu çağırın `rome_relayUnsignedV1` aynı yetkilendirme imzasıyla: proxy bunu oluşturur ve gönderir, Solana ücretlerini öder ve bir EVM işlem karmasıyla yanıt verir (Solana imzası değil). Sintetik, zincirin yerel gazını wei cinsinden öder; bu yüzden bakiyesi şundan düşükse `gasLimit x maxFeePerGas`, önce şununla doldurun `rome_composeExternalDepositV1` (bir cüzdan imzalı v1 işlemi; miktar pratikte 10^12 wei'nin katı olmalıdır), uygulamada cüzdan başına bir kez. Revert, ağ hatası veya reddedilen imza gibi diğer herhangi bir hata yeniden fırlatılır, relay edilmez. Ayrıntılar şurada: [Yönlendirilen çağrılar ve uzun çağrılar](/tr/gelistirici-kilavuzlari/call-evm-from-solana.md#relayed-calls-and-long-calls).

Kullanıcı şudur: **ücret ödeyen** ve doğrudan bir çağrı için Solana cüzdanından lamport (SOL) öder; kontrol ettiği sintetik varlıkları tutar ve asla SOL tutmaz. Her hat gönderiminden sonra, bir sonrakinden önce EVM nonce'unu yoklayın — indexer Solana onayının biraz gerisinde kalır ve aksi halde sonraki çağrı, zincirin zaten harcadığı bir nonce üzerine kurulurdu.

> **Önce atomik, reddedilirse relay.** Her çağrı için varsayılan, kullanıcının cüzdanı tarafından imzalanan, bir Solana işlemi içinde yürütülen tek bir EVM işlemidir. Relay, sığmayan bir çağrı için istisnadır; tercihe göre seçilecek ikinci bir yol değildir: doğrudan çağrı iki istemdir, relay edilmiş çağrı bir istemdir ve relay edilmiş çağrı lamport yerine Rome gazı ile ödenir. Çok adımlı bir yolculuk (onayla, sonra satın al) birkaç tam çağrıdır; her biri kendi işlemi olan ayrı çağrılardır, işlemler arasında bölünmüş tek bir yürütme değildir.

**RWA'nın neden sınırların dışına sızamayacağı.** Varlık EVM durumunda barınır — bakiyeleri Rome EVM programının hesapları içinde depolanır. Burada **hiçbir SPL basımı** ona ait ve taşınacak Solana-yerel hiçbir şey yoktur; hattın varlık taşıma ilkelleri SPL token hesapları üzerinde çalışır, EVM depolama bakiyesinin ise böyle bir hesabı yoktur. Solana imzasının ifade edebildiği her hareket token sözleşmesine yapılan bir çağrıdır; bu sözleşme allowlist kapısını çalıştırır — fonlanmış paket, whitelisted olmayan bir adrese transferin, EVM anahtarıyla mı yoksa Solana anahtarıyla mı imzalanırsa imzalansın aynı şekilde revert ettiğini doğrular. Nakit ayağı (wUSDC) bilinçli istisnadır: SPL desteklidir ve *çalışmaz* kullanıcının kendi token hesabına süpürülür.

***

## 7. Adım — Dağıtımdan sonra yetkiyi kimin tuttuğunu bilin

`ArcToken.initialize` proxy constructor'ından (tx 1) çalışır ve rolleri şu kişiye verir: `msg.sender` — sihirbaz sürücüsüne, yani ihraç edene. Şunları verir: **beş** rolü açıkça:

```solidity
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
_grantRole(MANAGER_ROLE, msg.sender);
_grantRole(YIELD_MANAGER_ROLE, msg.sender);
_grantRole(YIELD_DISTRIBUTOR_ROLE, msg.sender);
```

Bir geliştiricinin kaçırmaması gereken iki sonuç:

* **`MINTER_ROLE`, `BURNER_ROLE`ve `UPGRADER_ROLE` initialize sırasında VERİLMEZ.** İlk arz şu içinde basılır: `başlat` (bir dahili `_mint`, rol gerekmez), fakat bir *daha sonra* `mint` veya `yakma` ihracçının önce bu rolü kendine vermesini gerektirir. Bunu yapabilirler, çünkü şuna sahiptirler: `DEFAULT_ADMIN_ROLE` (her rolün yöneticisi — hiçbir şey `_setRoleAdmin`, bu yüzden `DEFAULT_ADMIN_ROLE` hepsini yönetir).
* **`UPGRADER_ROLE` fabrikaya asla verilmez.** Fabrika aracılı yükseltmeler (`ArcTokenFactoryV2.upgradeToken`) şunu gerektirir: **fabrikanın kendisinin** şuna sahip olmasını: `UPGRADER_ROLE` token üzerinde — bunu kendinize vermeniz hiçbir şey yapmaz. Bu izin, fabrikanın geri alamayacağı tek yönlü bir kapıdır, bu yüzden isteğe bağlıdır: fabrika aracılı yükseltmeler istiyorsanız, `grantRole(UPGRADER_ROLE, factory)` açıkça verin; aksi halde ihraççı token'ı doğrudan yükseltir.

Tam 21-yetki modeli (altı sözleşme boyunca 11 rol adı — aynı ad her sözleşmede farklı bir yetkidir) şurada kodlanmıştır: `arc/roles.ts`. Şunu sorun: `rolesRequiredFor(contract, functionName)` bir rolün kendi sözleşmesini okumak yerine, çünkü dört storefront işlevi ve her iki fabrika işlevi de üzerinde bulunan bir yetki tarafından sınırlandırılır *farklı* sözleşmede.

***

## 8. Adım — Getiriyi dağıtın

Token getiriyi wUSDC ile öder (şurada sabitlenmiştir: `başlat`, şununla değiştirilebilir: `setYieldToken` altında `YIELD_MANAGER_ROLE`)

```solidity
function distributeYieldWithLimit(uint256 totalAmount, uint256 startIndex, uint256 maxHolders)
    external onlyRole(YIELD_DISTRIBUTOR_ROLE) nonReentrant
    returns (uint256 nextIndex, uint256 totalHolders, uint256 amountDistributed);
```

Bunu doğru çalıştırmak için önemli olan mekanikler:

* **Tam `totalAmount` yalnızca şu durumda çağırandan çekilir: `startIndex == 0` pencere** (`safeTransferFrom(msg.sender, this, totalAmount)` ). İlk pencereden önce bu toplamı token'a onaylayın; sonraki pencereler zaten elde tutulan bakiyeden ödenir.
* **`nextIndex` şuna döner: `0` yürüyüş tamamlandığında.** Döngü `distributeYieldWithLimit(total, nextIndex, …)` şuna kadar `nextIndex` geri gelir `0`.
* **Getiri kısıtlı sahiplerin payları token sözleşmesinde kalır** — yeniden dağıtılmaz, atlanır.

> **Rome'da pratik üst sınır `maxHolders = 1` işlem başına** — 2. adımdaki aynı 62 hesap sınırı — bu yüzden getiri işlem başına bir sahip olacak şekilde yürütülür (sahip başına ölçülen \~318 K gaz). Whitelist'e alınmış her sahip, EVM olsun Solana-native olsun, orantılı pay alır; referans betik şudur: `measure-yield-batch.ts` ve fonlanmış smoke'un getiri adımı bunu uçtan uca doğrular.

```bash
# USAGE.md referans biçimi: nextIndex 0'a dönene kadar işlem başına bir sahip ilerle
distributeYieldWithLimit(total, offset, 1)
```

***

## 9. Adım — Dağıttığınızı doğrulayın

Rome'da hiçbir şey kaynakları otomatik olarak hiçbir yere göndermediği için doğrulama sizin çalıştırdığınız bir adımdır. Herhangi bir dağıtımdan sonra — altyapı ya da yeni bir token — salt okunur, anahtarsız, idempotent doğrulayıcıyı çalıştırın (zaten doğrulanmış sözleşmeler atlanır, bu yüzden bir token verdikten sonra tekrar çalıştırmak yalnızca yenilerini doğrular):

```bash
cd scripts && CHAIN_ID=<id> npx tsx verify-deployments.ts
```

Şunu bekler: **üç** token başına sözleşmeleri (proxy + her iki modül) ve altyapıyı bekler; dağıtımı başarısız kılmak yerine doğrulayamadıklarını raporlar. Bunu elle bir kez yapmak yedi sözleşmeyi kaçırdı.

Varlığı Rome kayıt defterine kaydetmek, doğrulamayı bir **kapı** değil, süsleme olarak değil (`register-asset.ts`): bir token şunu kazanır: `apps/arc/<chain>.json` kaydı — şu bilgileri taşıyan `standard: arc-permissioned-erc20` ve bir `transferRestriction` allowlist kapısını adlandıran — ancak üç sözleşmesinin de Sourcify üzerinde doğrulanmasıyla. Bu kayıt, gerçeklerini zincirden okur (sihirbazla dağıtılmış bir token'ın kaydedilmiş bir makbuzu yoktur) ve yalnızca yerel bir kayıt defteri checkout'u yazar; PR'ı sonuçlandırmak bir insanın işidir.

```bash
CHAIN_ID=200010 npx tsx register-asset.ts <tokenAddress>        # doğrula + kayıt kaydını hazırla
CHAIN_ID=200010 DRY=1 npx tsx register-asset.ts <tokenAddress>  # salt okunur önizleme
```

Tüm döngünün uçtan uca kanıtı için — oluştur, allowlist'e al, kapıla, her iki hatta da sat, getiri dağıt — fonlanmış smoke'u herhangi bir zincirde çalıştırın:

```bash
PRIVATE_KEY=… CHAIN_ID=… SOLANA_KEYPAIR=… npx tsx smoke.ts
```

***

## 10. Adım — Zincir dışında ne kaldığını bilin

Güven modeli bilerek dürüsttür ve bir geliştirici bunu kendi kullanıcılarına da aynı şekilde yansıtmalıdır:

* **Allowlist bayrağı sizin zincir dışı kararınızdır.** KYC/AML kendi sürecinizde gerçekleşir — sizin sağlayıcılarınız, sizin kurallarınız. Zincir yalnızca şunu kaydeder ve uygular: *sonucu*, `isWhitelisted(addr)`.
* **Zincir üstündeki tek durum, adres başına bir booleandır.** PII yok, belge yok, kimlik iddiası yok, zincir üstü atıf yok, bir adresin kime ait olduğuna dair kriptografik kanıt yok. Bu, Plume üzerinde kullanılan Arc modelidir; zincir üstü kimlik standardının (ör. ERC-3643) daha ağır transferlerini daha hafif mekanikler ve ihraççıda tutulan güven için takas eder.
* **Kurtarma, bir ihraççı yetkisidir; kaçış kapağı değildir.** Kayıp bir cüzdan — EVM ya da Solana — yedek bir adresi allowlist'e alarak ve gerekirse kendi hukuki süreciniz altında mint/burn/upgrade kullanarak kurtarılır (Arc'ın yerleşik zorunlu transfer ilkeli yoktur). Bu nedenle ihraççının anahtar saklaması uyumluluk duruşunun bir parçasıdır.
* **Bloom'un kendine ait bir relayer'ı yok, saklama yüzeyi yok.** Kullanıcılar her iki hatta da kendi gazlarını kendileri tutar. Uygulamanın işlettiği bir fee-payer hizmeti anahtar tutmak anlamına gelir — bir RWA ürününün eklememesi gereken bir yüzey. Solana hattındaki tek relay, bir işleme sığmayacak kadar büyük bir çağrı için zincir proxy'sinindir; kullanıcı yine de bunu kendi imzasıyla yetkilendirir ve kendi Rome gazı ile öder.

***

## Referans: araçlar

Her betik araçların `scripts/` dizininden çalışır; şu değişkenler `PRIVATE_KEY`, `CHAIN_ID`ve `REGISTRY_ROOT` dışa aktarılır.

| Betik                           | Ne yapar                                                                                      |
| ------------------------------- | --------------------------------------------------------------------------------------------- |
| `deploy-infra.ts`               | Her zincir için bir kez: wUSDC için bağlanmış dört ortak sözleşme.                            |
| `create-token.ts`               | Parçalı sihirbaz — sekiz işlem, bir token.                                                    |
| `rotate-factory.ts`             | Bir derleyici/artifakt değişikliğinden sonra yeni bir fabrika dağıtın; yeniden bağlayın.      |
| `verify-artifact-provenance.ts` | Proxy artifaktınızın dağıtılmış fabrika tarafından kabul edildiğini kanıtlayın (salt okunur). |
| `verify-deployments.ts`         | Sourcify üzerinde token başına üç sözleşmenin tamamını + altyapıyı doğrulayın (salt okunur).  |
| `register-asset.ts`             | Doğrulama kapısından geçirin, ardından kayıt uyumluluk kaydını hazırlayın.                    |
| `measure-yield-batch.ts`        | Bir zincirde işlem başına getiri tavanını yeniden ölçün.                                      |
| `smoke.ts`                      | Her iki hattı kapsayan fonlanmış uçtan uca paket.                                             |
| `sweep-synthetic-native.ts`     | Bir sintetik hesaptan yerel gazı geri alın (test temizliği).                                  |

Daha derin arka plan bu rehberin yanında yer alır: `docs/ARCHITECTURE.md` (katmanlı uygulama modeli), `docs/LANES.md` (iki hat ve saklama), `docs/COMPLIANCE.md` (güven modeli), ve `docs/USAGE.md` (operatör runbook'u).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.rome.builders/tr/rome-uzerindeki-uygulamalar/bloom/building.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
