Polkadot ağında ink! dilini kullanarak eşsiz bir şiir müzayedesi akıllı sözleşmesi geliştirmek ister misiniz? Bu rehber, temel kavramlardan ileri düzey uygulamalara kadar adım adım size yol gösterecek ve dijital sanatın blok zinciri ile nasıl buluştuğunu gösterecek.
Geleneksel sanat dünyası, dijitalleşmenin ve özellikle blok zinciri teknolojisinin yükselişiyle köklü bir dönüşüm geçiriyor. Bir zamanlar fiziksel sergi salonlarına, galerilere veya basılı yayınlara hapsolmuş olan sanat eserleri, artık dijital formatlarda küresel kitlelere ulaşabiliyor. Bu dönüşümde NFT’ler (Non-Fungible Token) önemli bir rol oynadı; dijital sanat eserlerinin benzersizliğini ve sahipliğini blok zinciri üzerinde ispatlamanın bir yolu olarak öne çıktı. Ancak, NFT’ler genellikle görsel sanatlar veya müzik gibi alanlarda yoğunlaşırken, edebiyatın ve özellikle de şiirin bu yenilikçi dalgadan yeterince faydalanamadığı bir gerçek. Peki, bir şairin eşsiz dizeleri, bir ressamın tablosu ya da bir müzisyenin bestesi kadar dijital dünyada değer ve sahiplik bulamaz mı?
İşte tam da bu noktada, şiir müzayedesi akıllı sözleşmeleri devreye giriyor. Geleneksel yayıncılık modelleri genellikle sanatçılara eserleri üzerinde sınırlı kontrol ve şeffaflık sunar. Telif hakları, dağıtım, satış süreçleri karmaşık ve merkezi yapılar tarafından yönetilir. Oysa blok zinciri, sanatçılara eserlerinin yaratıcılığını doğrudan okuyucularına veya koleksiyonerlerine ulaştırma fırsatı verirken, her bir şiirin özgünlüğünü ve sahipliğini inkar edilemez bir şekilde kaydetme imkanı sunar. Polkadot gibi ileri düzey bir blok zinciri ağı, bu vizyonu gerçeğe dönüştürmek için mükemmel bir zemin sağlar. Polkadot, tek bir blok zincirinin sınırlamalarının ötesine geçerek, ölçeklenebilirlik, birlikte çalışabilirlik ve özelleştirilebilir blok zincirleri (parachain’ler) aracılığıyla eşsiz bir altyapı sunar. Bu, sanatçıların kendi özel ihtiyaçlarına göre tasarlanmış, düşük maliyetli ve yüksek performanslı bir ortamda akıllı sözleşmelerini çalıştırmalarına olanak tanır. Örneğin, bir şiir müzayedesi platformu, kendi parachain’inde veya Polkadot Relay Chain’e bağlı bir akıllı sözleşme platformunda (örneğin Astar veya Moonbeam) çalışabilir, bu da platformun yoğunluğuna rağmen hızlı ve verimli işlemler yapılmasını sağlar.
Şiirlerin blok zincirinde değer bulması, sadece finansal bir getiri meselesi değildir; aynı zamanda sanatçı-koleksiyoner arasındaki ilişkiyi yeniden tanımlar. Her bir şiir, bir dijital varlık olarak, sanatçının imzasıyla mühürlenir ve bu mühür, zaman içinde taklit edilemez bir şekilde varlığını sürdürür. Bu sayede, sanatçılar eserlerinin telif haklarını daha etkin bir şekilde yönetebilir, her satıştan veya yeniden satıştan belirli bir yüzde alabilirler. Ayrıca, eserlerin tarihçesi, sahiplik değişiklikleri ve fiyat performansları şeffaf bir şekilde blok zincirinde görülebilir, bu da piyasa güvenini artırır ve spekülasyonu azaltır. Polkadot’un sunduğu çapraz zincir yetenekleri sayesinde, bir şiir sadece Polkadot ekosistemi içinde değil, diğer blok zincirleri arasında da transfer edilebilir veya farklı uygulamalarla entegre edilebilir. Bu, dijital şiir koleksiyonculuğunu küresel ve dinamik bir hale getirir, sanatçılara daha geniş bir erişim ve yeni gelir modelleri sunar. Dijital sanatın geleceğinde şiir, Polkadot’un güçlü altyapısı ve ink! gibi geliştirici dostu araçlarıyla hak ettiği değeri bulmaya hazır.
ink! ve Polkadot Akıllı Sözleşmelerine Giriş: Temelleri Anlamak
Blok zinciri dünyasına yeni adım atanlar veya mevcut bilgiyi pekiştirmek isteyenler için, Polkadot ekosisteminde akıllı sözleşme geliştirmek, ink! adı verilen özel bir Rust tabanlı etki alanına özgü dil (DSL) ile gerçekleşir. ink!, geliştiricilere güvenli, performanslı ve kolayca denetlenebilir akıllı sözleşmeler yazma imkanı sunar. Neden Rust? Çünkü Rust, bellek güvenliği ve performans konusunda eşsiz avantajlar sunar, bu da blok zinciri gibi kritik uygulamalar için ideal bir seçim olmasını sağlar. ink! ile yazılan kod, WebAssembly (Wasm) formatına derlenir ve Polkadot’un EVM uyumlu olmayan, daha genel ve esnek akıllı sözleşme platformlarında çalışır. Bu, ink!’i Ethereum’daki Solidity’ye bir alternatif ve Polkadot’un kendi akıllı sözleşme katmanı için birincil dil yapar.
Polkadot’ta akıllı sözleşmelerin nasıl çalıştığını anlamak için, öncelikle Polkadot’un mimarisine kısaca değinmek gerekir. Polkadot, Relay Chain adı verilen ana bir zincir ve bu zincire bağlanan, parachain adı verilen özelleştirilmiş blok zincirlerinden oluşur. Akıllı sözleşmeler doğrudan Relay Chain üzerinde çalışmaz; bunun yerine, parachain’ler veya Substrate tabanlı akıllı sözleşme platformları (örneğin Astar, Shiden, Moonbeam) üzerinde konuşlandırılır. Substrate, özel blok zincirleri oluşturmak için bir çerçevedir ve ink! akıllı sözleşmeleri, Substrate tabanlı bir runtime modülü olan pallet-contracts aracılığıyla bu zincirlerde yürütülür. Bu yapı, sözleşmelerin daha düşük gaz ücretleri, daha yüksek işlem hacmi ve belirli bir parachain’in özelleştirilmiş mantığına erişim gibi avantajlardan faydalanmasını sağlar.
Geliştirme ortamınızı kurmak oldukça basittir. İlk olarak, Rust geliştirme ortamına ihtiyacınız var. Bunu rustup kullanarak kolayca yapabilirsiniz:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update
rustup install nightly
rustup component add rust-src --toolchain nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
Ardından, ink! akıllı sözleşmeleri için gerekli olan cargo contract aracını kurmanız gerekir:
cargo install cargo-contract --force --locked
Bu araç, ink! projeleri oluşturmak, derlemek, test etmek ve dağıtmak için kullanılır. Kurulum tamamlandıktan sonra, ilk ink! projenizi oluşturmak için cargo contract new my_first_contract komutunu kullanabilirsiniz.
Bir ink! sözleşmesinin temel yapısı, Rust'ın güçlü makro sistemini kullanarak belirlenir. Her ink! sözleşmesi, #[ink::contract] niteliği ile işaretlenmiş bir ana modül içinde yer alır. Bu modülün içinde, sözleşmenin durumunu saklayan #[ink(storage)] niteliğiyle işaretlenmiş bir yapı bulunur. Bu yapı, bir nevi veritabanı gibidir ve sözleşmenin tüm önemli verilerini barındırır. Örneğin, bizim şiir müzayedesi sözleşmemizde, şiirlerin listesi, aktif müzayedeler, teklifler gibi bilgiler burada saklanacaktır. Sözleşmenin işlevselliğini tanımlayan metodlar ise #[ink(message)] niteliğiyle işaretlenir. Bu metodlar, blok zinciri dışından çağrılabilen ve sözleşmenin durumunu değiştirebilen veya sorgulayabilen halka açık fonksiyonlardır. Kurucu (constructor) fonksiyonlar da #[ink(constructor)] niteliğiyle işaretlenir ve sözleşme ilk kez dağıtıldığında çağrılır. Ayrıca, sözleşmede belirli olayların gerçekleştiğini bildirmek için #[ink(event)] niteliğiyle işaretlenmiş yapılar tanımlanabilir. Bu olaylar, dış uygulamaların (frontend) sözleşmedeki değişiklikleri izlemesine olanak tanır. ink!'in sağladığı bu yapısal elemanlar, geliştiricilere güçlü ve modüler akıllı sözleşmeler oluşturma esnekliği sunar, aynı zamanda Rust'ın bellek güvenliği garantileri sayesinde potansiyel güvenlik açıklarını minimize eder.
Şiir Müzayedesi Sözleşmesi İçin Temel Yapı Taşları Nelerdir?
Bir şiir müzayedesi akıllı sözleşmesi oluştururken, projemizi oluşturan temel bileşenleri ve onların nasıl etkileşim kuracağını iyi anlamamız gerekir. Bu sözleşmenin ana hedefi, şairlerin dijital şiirlerini listeleyebileceği, koleksiyonerlerin bu şiirler için teklif verebileceği ve belirli bir süre sonunda en yüksek teklifi veren kişinin şiirin dijital sahipliğini kazanacağı şeffaf ve otomatik bir sistem sunmaktır. Bu temel işlevselliği sağlamak için sözleşmemizin bazı kilit özelliklere ve veri yapılarına sahip olması gerekir.
Sözleşmemizin en temel durum değişkeni (storage), müzayedeye sunulacak şiirleri ve devam eden müzayedeleri tutmaktır. Her bir şiir, benzersiz bir kimlik (ID), başlık, içerik, sanatçı bilgisi ve listelenip listelenmediğini belirten bir durum ile temsil edilebilir. Bu, bir tür basit NFT (Non-Fungible Token) yapısına benzer, ancak burada "NFT" terimi yerine "dijital şiir varlığı" terimini kullanmak daha uygun olabilir. Ayrıca, her bir aktif müzayede için de ayrı bir durum kaydı tutulmalıdır. Bu kayıt, hangi şiirin müzayede edildiğini, müzayedenin başlangıç ve bitiş zamanlarını, mevcut en yüksek teklifi ve en yüksek teklifi vereni, müzayedenin aktif olup olmadığını ve sanatçının ödeme adresini içermelidir. Bu sayede, tüm müzayede süreci blok zinciri üzerinde şeffaf bir şekilde izlenebilir ve yönetilebilir.
Temel işlevler veya mesajlar, sözleşmenin dış dünya ile nasıl etkileşime gireceğini belirler. İlk olarak, şairlerin sözleşmeye yeni şiirler ekleyebilmesi için bir fonksiyona (add_poem) ihtiyacımız var. Bu fonksiyon, şiirin metadata'sını (başlık, içerik vb.) alacak ve onu sözleşmenin depolama alanına kaydedecektir. İkinci olarak, bir şiirin müzayedesini başlatmak için bir start_auction fonksiyonu gereklidir. Bu fonksiyon, şiir ID'sini, başlangıç fiyatını ve müzayedenin ne kadar süreceğini alarak yeni bir müzayede kaydı oluşturacaktır. En kritik işlevlerden biri, elbette, teklif verme mekanizmasıdır. place_bid fonksiyonu, bir kullanıcının belirli bir müzayedeye teklif vermesini sağlar. Bu fonksiyonun, teklifin mevcut en yüksek tekliften yüksek olup olmadığını kontrol etmesi ve önceki en yüksek teklif sahibine parasını iade etmesi gibi önemli mantıkları içermesi gerekir. Son olarak, bir müzayedenin süresi dolduğunda, end_auction fonksiyonu çağrılarak kazanan belirlenmeli ve şiirin sahipliği en yüksek teklifi verene aktarılırken, elde edilen gelirin sanatçıya (ve varsa platforma) aktarılması sağlanmalıdır. Bu, sanatçıya telif hakkı gelirlerini doğrudan ve güvenli bir şekilde alma imkanı sunar.
Sözleşmemizde gerçekleşen önemli olayları bildirmek için olay tanımlamaları (events) kullanmak, dış uygulamaların (örneğin bir web arayüzü) sözleşmedeki değişikliklere gerçek zamanlı olarak tepki vermesini sağlar. Örneğin, bir şiir eklendiğinde (PoemAdded), bir teklif verildiğinde (BidPlaced) veya bir müzayede sonuçlandığında (AuctionEnded) olaylar yayabiliriz. Bu olaylar, kullanıcılara anlık bildirimler göndermek, bir müzayede panosunu güncellemek veya blok zinciri kaşiflerinde sözleşme hareketlerini görüntülemek için çok önemlidir. Bu temel yapı taşlarını doğru bir şekilde tanımlayarak ve uygulayarak, sağlam, güvenilir ve kullanıcı dostu bir şiir müzayedesi akıllı sözleşmesi oluşturabiliriz. Her bir bileşenin dikkatli tasarımı, sözleşmenin uzun vadeli başarısı ve güvenliği için kritik öneme sahiptir.
Adım Adım ink! ile Şiir Müzayedesi Akıllı Sözleşmesi Geliştirme
Şimdi teorik bilgileri pratiğe dökelim ve ink! kullanarak bir şiir müzayedesi akıllı sözleşmesi geliştirmeye başlayalım. İlk adım, yeni bir ink! projesi oluşturmaktır. Terminalinizi açın ve aşağıdaki komutu çalıştırın:
cargo contract new poetry_auction
cd poetry_auction
Bu komut, sizin için temel bir ink! sözleşme yapısı oluşturacaktır. src/lib.rs dosyasını açtığınızda, bir boilerplate kod göreceksiniz. Şimdi bu kodu, şiir müzayedesi mantığını içerecek şekilde değiştirelim.
Öncelikle, sözleşmemizin temel durumunu (storage) tanımlayalım ve şiirler ile müzayedeler için özel veri yapıları (struct) oluşturalım. Bu yapılar, blok zinciri üzerinde saklanacak verilerin nasıl organize edileceğini belirler.
// src/lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::new_without_default)]
use ink_lang as ink;
#[ink::contract]
mod poetry_auction {
use ink_storage::collections::HashMap;
use ink_env::CallFlags; // Para transferleri için
use ink_prelude::string::String;
use ink_prelude::vec::Vec; // Gerekirse kullanılabilir
/// Sözleşme hata türleri
#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(ink_primitives::LangError))]
pub enum ContractError {
/// Yetkilendirme hatası
Unauthorized,
/// Şiir bulunamadı
PoemNotFound,
/// Müzayede bulunamadı
AuctionNotFound,
/// Müzayede aktif değil
AuctionNotActive,
/// Geçersiz müzayede süresi
InvalidAuctionTime,
/// Teklif çok düşük
BidTooLow,
/// Müzayede henüz bitmedi
AuctionNotEnded,
/// Zaten bitmiş müzayede
AuctionAlreadyEnded,
/// Müzayede zaten başlamış
AuctionAlreadyStarted,
/// Para transferi başarısız oldu
TransferFailed,
/// Şiir zaten listelenmiş
PoemAlreadyListed,
}
/// Akıllı sözleşmede kullanılacak genel Result tipi
pub type Result = core::result::Result;
/// Bir şiiri temsil eden yapı
#[derive(scale::Encode, scale::Decode, scale::TypeInfo)]
#[cfg_attr(feature = "std", derive(Debug, PartialEq, Eq))]
pub struct Poem {
id: u32,
title: String,
content: String,
artist: AccountId,
is_listed: bool, // Müzayedede listelenmiş mi?
}
/// Bir müzayedeyi temsil eden yapı
#[derive(scale::Encode, scale::Decode, scale::TypeInfo)]
#[cfg_attr(feature = "std", derive(Debug, PartialEq, Eq))]
pub struct Auction {
poem_id: u32,
start_time: u64, // Müzayedenin başladığı blok zamanı
end_time: u64, // Müzayedenin biteceği blok zamanı
highest_bid: Balance,
highest_bidder: AccountId,
is_active: bool,
artist_payout_address: AccountId,
previous_highest_bidder: Option<(AccountId, Balance)>, // Önceki en yüksek teklif sahibinin iade edilecek miktarı
}
/// Sözleşmenin ana depolama yapısı
#[ink(storage)]
pub struct PoetryAuction {
owner: AccountId,
poems: HashMap,
auction_id_counter: u32,
active_auctions: HashMap,
poem_counter: u32, // Benzersiz şiir ID'leri için
}
/// Müzayede olayları
#[ink(event)]
pub struct PoemAdded {
#[ink(topic)]
poem_id: u32,
#[ink(topic)]
artist: AccountId,
title: String,
}
#[ink(event)]
pub struct AuctionStarted {
#[ink(topic)]
poem_id: u32,
#[ink(topic)]
auction_id: u32, // Müzayede ID'si olmasa da şiir ID'si yeterli olabilir
end_time: u64,
initial_bid: Balance,
}
#[ink(event)]
pub struct BidPlaced {
#[ink(topic)]
poem_id: u32,
#[ink(topic)]
bidder: AccountId,
amount: Balance,
previous_highest_bidder: Option,
previous_highest_bid: Balance,
}
#[ink(event)]
pub struct AuctionEnded {
#[ink(topic)]
poem_id: u32,
#[ink(topic)]
winner: AccountId,
final_bid: Balance,
}
impl PoetryAuction {
/// Sözleşmenin kurucusu, sözleşmeyi dağıtan hesabı sahibi olarak ayarlar.
#[ink(constructor)]
pub fn new() -> Self {
Self {
owner: Self::env().caller(),
poems: HashMap::new(),
auction_id_counter: 0,
active_auctions: HashMap::new(),
poem_counter: 0,
}
}
}
Yukarıdaki kod bloğu, sözleşmemizin veri yapılarını ve bir kurucu fonksiyonunu tanımlar. Poem ve Auction yapıları, bir şiirin ve bir müzayedenin temel özelliklerini tutar. PoetryAuction depolama alanı ise tüm şiirleri ve aktif müzayedeleri içerir. Ayrıca, sözleşmemizin olası hata durumlarını yönetmek için bir ContractError enum'u ve dış uygulamaların izleyebileceği olaylar (events) tanımladık. Özellikle HashMap kullanarak dinamik olarak öğeler ekleyip çıkarabiliriz.
Şimdi, sözleşmemizin temel işlevlerini implement edelim:
impl PoetryAuction {
// ... (yukarıdaki constructor ve storage tanımları) ...
/// Yeni bir şiiri sözleşmeye ekler. Sadece sahibinin (owner) erişimine açıktır.
#[ink(message)]
pub fn add_poem(&mut self, title: String, content: String) -> Result {
let caller = Self::env().caller();
if caller != self.owner {
return Err(ContractError::Unauthorized);
}
self.poem_counter += 1;
let new_poem_id = self.poem_counter;
let poem = Poem {
id: new_poem_id,
title: title.clone(),
content,
artist: caller,
is_listed: false,
};
self.poems.insert(new_poem_id, poem);
Self::env().emit_event(PoemAdded {
poem_id: new_poem_id,
artist: caller,
title,
});
Ok(new_poem_id)
}
/// Bir şiir için müzayede başlatır. Sadece şiirin sanatçısı veya sahibi başlatabilir.
#[ink(message)]
pub fn start_auction(&mut self, poem_id: u32, duration_blocks: u64, initial_bid: Balance) -> Result<()> {
let mut poem = self.poems.get_mut(&poem_id).ok_or(ContractError::PoemNotFound)?;
if poem.artist != Self::env().caller() && self.owner != Self::env().caller() { // Sanatçı veya sahibi başlatabilir
return Err(ContractError::Unauthorized);
}
if poem.is_listed {
return Err(ContractError::PoemAlreadyListed);
}
if duration_blocks == 0 {
return Err(ContractError::InvalidAuctionTime);
}
if self.active_auctions.values().any(|auction| auction.poem_id == poem_id && auction.is_active) {
return Err(ContractError::AuctionAlreadyStarted);
}
let current_block_timestamp = Self::env().block_timestamp();
let end_time = current_block_timestamp + duration_blocks; // Basitlik için block timestamp kullanıyoruz
self.auction_id_counter += 1;
let new_auction_id = self.auction_id_counter;
let auction = Auction {
poem_id,
start_time: current_block_timestamp,
end_time,
highest_bid: initial_bid,
highest_bidder: AccountId::from([0x0; 32]), // Başlangıçta boş veya sözleşmenin kendi adresi
is_active: true,
artist_payout_address: poem.artist,
previous_highest_bidder: None,
};
self.active_auctions.insert(new_auction_id, auction);
poem.is_listed = true; // Şiiri listelenmiş olarak işaretle
Self::env().emit_event(AuctionStarted {
poem_id,
auction_id: new_auction_id,
end_time,
initial_bid,
});
Ok(())
}
/// Bir müzayedeye teklif verir.
#[ink(message, payable)]
pub fn place_bid(&mut self, poem_id: u32) -> Result<()> {
let mut auction_id_to_find = None;
for (id, auction) in self.active_auctions.iter() {
if auction.poem_id == poem_id && auction.is_active {
auction_id_to_find = Some(*id);
break;
}
}
let auction_id = auction_id_to_find.ok_or(ContractError::AuctionNotFound)?;
let mut auction = self.active_auctions.get_mut(&auction_id).ok_or(ContractError::AuctionNotFound)?;
if !auction.is_active || Self::env().block_timestamp() >= auction.end_time {
return Err(ContractError::AuctionNotActive);
}
let caller = Self::env().caller();
let current_bid = Self::env().transferred_value();
if current_bid <= auction.highest_bid {
return Err(ContractError::BidTooLow);
}
// Önceki en yüksek teklif sahibine parayı iade et
if auction.highest_bidder != AccountId::from([0x0; 32]) { // Eğer daha önce teklif veren varsa
let previous_bidder = auction.highest_bidder;
let previous_bid = auction.highest_bid;
match Self::env().transfer(previous_bidder, previous_bid) {
Ok(_) => {
auction.previous_highest_bidder = Some((previous_bidder, previous_bid));
},
Err(_) => {
// Transfer başarısız olursa ne yapacağımıza karar vermeliyiz.
// Basit bir senaryoda işlemi geri döndürebilir veya hatayı işleyebiliriz.
// Yeniden deneme mekanizması veya manuel kurtarma gerekebilir.
return Err(ContractError::TransferFailed);
}
}
}
let old_highest_bid = auction.highest_bid;
let old_highest_bidder = auction.highest_bidder;
auction.highest_bid = current_bid;
auction.highest_bidder = caller;
Self::env().emit_event(BidPlaced {
poem_id,
bidder: caller,
amount: current_bid,
previous_highest_bidder: if old_highest_bidder != AccountId::from([0x0; 32]) { Some(old_highest_bidder) } else { None },
previous_highest_bid: old_highest_bid,
});
Ok(())
}
/// Müzayedeyi sonlandırır ve kazananı belirler.
#[ink(message)]
pub fn end_auction(&mut self, poem_id: u32) -> Result<()> {
let mut auction_id_to_find = None;
for (id, auction) in self.active_auctions.iter() {
if auction.poem_id == poem_id && auction.is_active {
auction_id_to_find = Some(*id);
break;
}
}
let auction_id = auction_id_to_find.ok_or(ContractError::AuctionNotFound)?;
let mut auction = self.active_auctions.get_mut(&auction_id).ok_or(ContractError::AuctionNotFound)?;
if !auction.is_active {
return Err(ContractError::AuctionAlreadyEnded);
}
if Self::env().block_timestamp() < auction.end_time {
return Err(ContractError::AuctionNotEnded);
}
// Müzayedeyi devre dışı bırak
auction.is_active = false;
if auction.highest_bidder == AccountId::from([0x0; 32]) {
// Hiç teklif verilmediyse, şiiri tekrar listesiz yap
if let Some(poem) = self.poems.get_mut(&poem_id) {
poem.is_listed = false;
}
// Bu durumda bir kazanan yok, sadece müzayede pasif hale gelir
} else {
// En yüksek teklifi veren, şiirin yeni sahibi olur
if let Some(mut poem) = self.poems.get_mut(&poem_id) {
poem.artist = auction.highest_bidder; // Şiirin sanatçısı artık kazanan
// Burada poem.artist'i değiştirmek yerine, şiirin orijinal sahibini tutan
// ve NFT gibi transfer edilebilir bir 'sahip' alanı olan bir yapı kullanmak daha iyi olurdu.
// Basitlik için burada artist'i güncelliyoruz.
}
// Sanatçıya ödeme yap
let artist_cut = auction.highest_bid; // Basitlik için tüm teklifi sanatçıya aktar
match Self::env().transfer(auction.artist_payout_address, artist_cut) {
Ok(_) => {},
Err(_) => {
// Kritik hata: Sanatçıya ödeme yapılamadı.
// Bu durumda işlemi geri almalı veya manuel müdahale için bir mekanizma olmalı.
// Basitlik için burada bir hata döndürüyoruz.
return Err(ContractError::TransferFailed);
}
}
Self::env().emit_event(AuctionEnded {
poem_id,
winner: auction.highest_bidder,
final_bid: auction.highest_bid,
});
}
Ok(())
}
/// Şiir detaylarını getirir (view fonksiyonu)
#[ink(message)]
pub fn get_poem_details(&self, poem_id: u32) -> Result {
self.poems.get(&poem_id).cloned().ok_or(ContractError::PoemNotFound)
}
/// Aktif bir müzayede varsa detaylarını getirir (view fonksiyonu)
#[ink(message)]
pub fn get_active_auction_details(&self, poem_id: u32) -> Result {
let auction_option = self.active_auctions.values().find(|auction| auction.poem_id == poem_id && auction.is_active);
auction_option.cloned().ok_or(ContractError::AuctionNotFound)
}
}
Bu kod, temel bir şiir müzayedesi akıllı sözleşmesinin nasıl görünebileceğini gösterir. add_poem fonksiyonu ile şiirler eklenir, start_auction ile bir şiir için müzayede başlatılır. place_bid fonksiyonu, gönderilen değeri (payable) kullanarak teklifleri yönetir ve önceki en yüksek teklif sahibine parasını iade eder. end_auction ise müzayedenin bitiş zamanını kontrol ederek kazananı belirler ve ilgili para transferlerini yapar. Ayrıca, şiir ve müzayede detaylarını sorgulamak için get_poem_details ve get_active_auction_details gibi okuma (view) fonksiyonları da ekledik. Bu sözleşme basit bir örnek teşkil ederken, gerçek dünya senaryolarında daha fazla özellik ve güvenlik önlemi içerebilir.
Vaka Analizi: "Dijital Şiir Koleksiyonu Oluşturma ve Sanatçı Gelirlerinin Şeffaflığı"
Düşünün ki 'Ethereal Dize' adında genç ve yetenekli bir şair, eserlerini dijital dünyada hak ettiği değere ulaştırmak istiyor. Geleneksel yayıncılık modelleri ona ya çok düşük telif ücretleri sunuyor ya da eserleri üzerindeki kontrolünü kısıtlıyor. 'Ethereal Dize', Polkadot tabanlı ink! ile geliştirilmiş bir şiir müzayedesi platformu kullanarak, en yeni şiiri "Yıldızların Fısıltısı"nı listeliyor. add_poem fonksiyonunu çağırarak şiirin başlığını ve içeriğini sözleşmeye ekliyor. Ardından, şiiri belirli bir başlangıç teklifi ve 24 saatlik bir süre ile start_auction fonksiyonu aracılığıyla müzayedeye çıkarıyor. Müzayede başladığında, dünya çapındaki koleksiyonerler, şiirin benzersizliğini ve blok zinciri üzerindeki kalıcı kaydını takdir ederek teklif vermeye başlıyorlar. Her yeni teklif verildiğinde, place_bid fonksiyonu, önceki en yüksek teklif sahibine hemen fonlarını iade ediyor, bu da koleksiyonerler için güvenli ve dinamik bir süreç sağlıyor. Müzayede süresi dolduğunda, 'Ethereal Dize' veya platform yöneticisi end_auction fonksiyonunu çağırıyor. En yüksek teklifi veren kişi, şiirin dijital sahipliğini kazanıyor ve şiir sözleşme içinde o kişinin hesabına işaretleniyor (veya daha gelişmiş bir NFT mantığıyla sahiplik transferi gerçekleşiyor). En önemlisi, şiirin satışından elde edilen gelirin tamamı veya üzerinde anlaşılan yüzdesi, şeffaf bir şekilde doğrudan 'Ethereal Dize'nin cüzdanına aktarılıyor. Bu sayede 'Ethereal Dize', eserlerinin değerini doğrudan belirleyebilir, telif haklarını koruyabilir ve aracılar olmadan gelir elde edebilir, bu da ona sanatına odaklanması için daha fazla özgürlük tanır.
Akıllı Sözleşme Güvenliği ve İleri Düzey İpuçları
Akıllı sözleşmeler, blok zincirinin değişmez doğası gereği, bir kez dağıtıldığında kolayca değiştirilemezler. Bu durum, onları inanılmaz derecede güçlü kılarken, aynı zamanda kritik güvenlik risklerini de beraberinde getirir. Küçük bir hata veya açık, büyük finansal kayıplara veya sözleşmenin tamamen işlevsiz hale gelmesine yol açabilir. Bu nedenle, ink! ile akıllı sözleşme geliştirirken güvenliği en öncelikli konu olarak ele almak elzemdir. İlk olarak, "reentrancy" saldırıları, akıllı sözleşme dünyasındaki en kötü şöhretli güvenlik açıklarından biridir. Bu tür saldırılar, bir sözleşmenin başka bir sözleşmeyle etkileşime girdiğinde, ikinci sözleşmenin ilk sözleşmeyi bitmeden tekrar çağırmasıyla meydana gelir ve fonların defalarca çekilmesine olanak tanır. ink! ve Rust'ın doğası gereği bu tür saldırılara karşı bir nebze daha dirençli olunsa da, özellikle transfer veya call gibi harici çağrılar yapılırken dikkatli olunmalıdır. En iyi pratik, "checks-effects-interactions" desenini uygulamaktır: önce tüm kontrolleri yapın, sonra sözleşmenin durumunu güncelleyin ve en son olarak harici çağrıları gerçekleştirin. Bu, sözleşme durumunun beklenmedik bir şekilde değiştirilmesini önler.
İkinci olarak, "integer overflow/underflow" problemleri de sıkça karşılaşılan güvenlik açıklarındandır. Bu durumlar, sayısal değişkenlerin taşıyabileceğinden daha büyük veya daha küçük bir değer aldığında meydana gelir. Örneğin, bir sayaca sürekli olarak 1 eklendiğinde ve bu sayaç maksimum değerine ulaştığında, bir sonraki ekleme işlemi sayacın en düşük değerine sıfırlanmasına (overflow) neden olabilir. ink! ve Rust'ta, çoğu durumda, varsayılan olarak bu tür taşmalar derleme zamanında veya çalışma zamanında bir panik hatasına yol açar. Ancak, özellikle matematiksel işlemler yaparken ve kullanıcı girdilerini işlerken bu durumun farkında olmak ve güvenli matematik kütüphaneleri kullanmak veya gerekli kontrolleri yapmak önemlidir. Örneğin, bakiye hesaplamalarında dikkatli olunmalı ve özellikle çıkarma işlemlerinde negatif bakiyelere düşülmediğinden emin olunmalıdır.
Yetkilendirme mekanizmaları, akıllı sözleşme güvenliğinin temelini oluşturur. only_owner veya only_artist gibi erişim kontrol mekanizmaları, belirli fonksiyonların yalnızca yetkili hesaplar tarafından çağrılabileceğini garantiler. Sözleşme içinde, Self::env().caller() fonksiyonunu kullanarak çağrı yapanın kimliğini alabilir ve bu kimliği önceden tanımlanmış yetkili hesaplarla karşılaştırabilirsiniz. Örneğin, add_poem fonksiyonunun yalnızca sözleşme sahibi tarafından çağrılabileceğinden emin olmak için bir kontrol ekledik. Bu kontroller, sözleşmenin istenmeyen kişiler tarafından manipüle edilmesini önler. Hata yönetimi de kritik öneme sahiptir. ink!, Rust'ın Result enum'unu kullanarak hata işlemeyi teşvik eder. Fonksiyonlarınızın başarılı veya başarısız olabileceği durumları açıkça belirtmek ve uygun hata mesajları döndürmek, sözleşmenizin davranışını daha öngörülebilir hale getirir ve kullanıcıların veya entegre uygulamaların hataları daha kolay anlamasına ve çözmesine yardımcı olur. Örneğin, BidTooLow gibi özel hata türleri, kullanıcılara neden tekliflerinin reddedildiğini açıkça bildirir.
Gaz optimizasyonu, özellikle Polkadot gibi bir ağda, her ne kadar gaz ücretleri Ethereum'dan daha düşük ve öngörülebilir olsa da, her zaman göz önünde bulundurulması gereken bir konudur. Veri depolama maliyetlidir, bu nedenle gereksiz verileri saklamaktan kaçınmak ve veri yapılarını mümkün olduğunca kompakt tutmak önemlidir. Döngüleri ve karmaşık hesaplamaları optimize etmek de gaz tüketimini azaltabilir. Son olarak, kapsamlı testler olmadan hiçbir akıllı sözleşme dağıtılmamalıdır. ink!, entegre test çerçevesi sağlar. #[ink::test] niteliğiyle işaretlenmiş test fonksiyonları yazarak sözleşmenizin her bir bölümünün beklendiği gibi çalıştığından emin olabilirsiniz. Bu testler, farklı senaryoları (başarılı işlemler, hata durumları, kenar durumlar) kapsayacak şekilde tasarlanmalıdır. Örneğin, müzayedenin bitiş zamanından önce teklif verilememesi veya bitiş zamanından sonra yeni tekliflerin reddedilmesi gibi durumları test etmek, sözleşmenizin sağlamlığını artırır. Güvenli kod yazma pratiği, sadece teknik bilgi gerektirmez; aynı zamanda potansiyel kötüye kullanım senaryolarını hayal etme ve proaktif önlemler alma becerisi de gerektirir.
Mobil Uyumlu Arayüz İçin HTML ve CSS Yaklaşımları
Akıllı sözleşmemizi başarıyla geliştirdikten ve Polkadot ağına dağıttıktan sonra, kullanıcıların bu sözleşmeyle kolayca etkileşime geçebileceği bir arayüze ihtiyacımız olacak. Bu arayüz, şairlerin şiirlerini eklemesine, koleksiyonerlerin müzayedeleri görüntülemesine ve teklif vermesine olanak tanımalıdır. Günümüz dünyasında mobil cihazların yaygınlığı göz önüne alındığında, geliştireceğimiz arayüzün mobil uyumlu (responsive) olması kritik öneme sahiptir. Kullanıcılarımızın masaüstü bilgisayarlarından, tabletlerinden veya akıllı telefonlarından sorunsuz bir deneyim yaşayabilmesi için HTML ve CSS yaklaşımlarımızı dikkatlice planlamalıyız.
Temel HTML yapısı, bir web uygulamasının omurgasını oluşturur. Şiir listeleme, müzayede detaylarını gösterme, teklif formu ve sanatçı profili gibi bileşenler için anlamsal HTML etiketleri (
, ,
,
,
) kullanmak, hem erişilebilirlik hem de SEO açısından önemlidir. Örneğin, bir şiir kartı için
etiketi içinde başlık (
), sanatçı adı (
), şiir içeriğinden bir alıntı ve mevcut teklif bilgilerini gösterebiliriz. Teklif verme işlemi için bir
etiketi kullanacak, içinde ve gibi elemanlara yer vereceğiz. Bu elemanlar, Polkadot.js veya benzeri bir JavaScript kütüphanesi aracılığıyla akıllı sözleşmemizle etkileşime geçecektir.
Mobil uyumluluğun ilk adımı, HTML belgemizin bölümüne viewport meta etiketini eklemektir:
Bu etiket, tarayıcıya sayfanın cihazın genişliğine göre ölçeklenmesini ve başlangıçta 1.0 oranında yakınlaştırmayı emreder. Bu olmadan, mobil cihazlar sayfayı masaüstü görünümünde render etmeye çalışabilir ve bu da kötü bir kullanıcı deneyimine yol açar.
CSS tarafında, mobil uyumluluğu sağlamak için "mobile-first" yaklaşımını benimsemek genellikle en iyisidir. Bu, önce küçük ekranlar (mobil cihazlar) için stil yazmak ve daha sonra daha büyük ekranlar için stilleri artırmak anlamına gelir. media query'ler, belirli ekran boyutlarına veya cihaz özelliklerine göre farklı CSS kuralları uygulamanızı sağlar. İşte basit bir örnek:
Yukarıdaki örnekte, .poem-card ve .bid-form gibi bileşenlerin nasıl flex-direction özelliğini kullanarak mobil ve masaüstü görünümleri arasında geçiş yaptığını görebilirsiniz. Flexbox (Esnek Kutu Modeli) veya CSS Grid (Izgara Sistemi), modern mobil uyumlu web tasarımı için güçlü araçlardır ve bileşenlerin farklı ekran boyutlarında düzenini kolayca ayarlamanıza olanak tanır. Flexbox, tek boyutlu (satır veya sütun) düzenler için idealdir, Grid ise iki boyutlu (satırlar ve sütunlar) düzenler için daha uygundur. Bu yapılar, web sayfanızın içeriğini cihazın ekran boyutuna göre otomatik olarak yeniden düzenlemesini sağlar, böylece kullanıcılar her cihazda optimize edilmiş bir deneyim yaşar.
Akıllı sözleşme ile etkileşimi sağlamak için ise Polkadot.js API gibi kütüphaneler kullanılır. Bu kütüphaneler, bir kullanıcının cüzdanıyla (örneğin Polkadot{.js} uzantısı) bağlantı kurarak, sözleşme üzerinde query (veri okuma) ve transaction (veri yazma/durum değiştirme) işlemleri yapılmasına olanak tanır. Frontend tarafında, React, Vue veya Svelte gibi modern JavaScript framework'lerini kullanarak dinamik ve etkileşimli bir arayüz oluşturabilirsiniz. Bu framework'ler, kullanıcı arayüzü bileşenlerini daha yönetilebilir hale getirir ve karmaşık durum yönetimini kolaylaştırır, bu da blok zinciri etkileşimli uygulamaların geliştirilmesini hızlandırır.
Sonuç: Dijital Sanatın Geleceğinde ink! ve Polkadot'un Rolü
Bu makalede, Polkadot ağı üzerinde ink! dilini kullanarak nasıl bir şiir müzayedesi akıllı sözleşmesi geliştireceğimizi adım adım inceledik. Dijital sanatın ve özellikle şiirin blok zinciri teknolojisiyle nasıl buluşabileceğini, sanatçılara eserleri üzerinde daha fazla kontrol ve şeffaf gelir akışları sağlayabileceğini keşfettik. ink!'in Rust tabanlı güvenli ve performanslı yapısı ile Polkadot'un ölçeklenebilirlik ve birlikte çalışabilirlik avantajlarının, bu tür yenilikçi uygulamalar için güçlü bir temel oluşturduğunu gördük. Temel sözleşme yapılarından, güvenlik ipuçlarına ve mobil uyumlu arayüz geliştirmeye kadar pek çok konuya değindik.
Şiir müzayedesi, blok zincirinin sadece finansal işlemlerde değil, aynı zamanda kültürel ve sanatsal alanlarda da dönüştürücü bir etkiye sahip olabileceğinin güzel bir örneğidir. Sanatçılar, eserlerinin değerini doğrudan belirleyebilir, telif haklarını şeffaf bir şekilde yönetebilir ve küresel bir kitleye ulaşabilirler. Koleksiyonerler ise, benzersiz dijital sanat eserlerine sahip olmanın yanı sıra, sanatçıları doğrudan destekleyerek sanatsal üretimin sürdürülebilirliğine katkıda bulunabilirler. Polkadot ekosisteminin sürekli gelişen yapısı ve ink!'in sunduğu esneklik, gelecekte çok daha karmaşık ve zengin dijital sanat platformlarının ortaya çıkmasına olanak tanıyacaktır. Bu sadece şiirle sınırlı kalmayıp, diğer edebiyat türleri, dijital heykeller, performans sanatları ve daha birçok alana yayılabilecek potansiyele sahiptir. Bu rehberin, kendi dijital sanat projelerinizi geliştirmek için size ilham vermesini ve Polkadot üzerinde ink! ile yaratıcılığınızı serbest bırakmanızı umuyoruz.
Sıkça Sorulan Sorular (SSS):
- ink! neden Rust tabanlıdır?
- ink!, Rust'ın bellek güvenliği, yüksek performansı ve geliştirici dostu özelliklerinden yararlanmak için Rust tabanlıdır. Bu, akıllı sözleşmelerde kritik olan güvenlik açıklarını en aza indirmeye ve verimli kod yazmaya yardımcı olur.
- Polkadot'ta gaz ücretleri nasıl çalışır?
- Polkadot'ta gaz ücretleri, Ethereum'daki gibi doğrudan bir "gas price" modeli yerine, işlemin karmaşıklığına ve kullanılan kaynaklara (depolama, hesaplama) dayalıdır. Parachain'e bağlı olarak bu ücretler daha düşük ve daha tahmin edilebilir olabilir, bu da kullanıcı deneyimini iyileştirir.
- Akıllı sözleşme dağıtımı sonrası güncelleme mümkün müdür?
- Evet, ink! akıllı sözleşmeleri genellikle "upgradable proxy" desenleri kullanılarak güncellenebilir. Bu, sözleşmenin mantığını içeren bir "implementation" sözleşmesinin ayrı olduğu ve bir "proxy" sözleşmesinin bu implementation'a işaret ettiği anlamına gelir. Proxy sözleşmesinin işaret ettiği implementation sözleşmesi değiştirilerek, ana sözleşme adresi değişmeden güncelleme yapılabilir.
- Bir şiiri NFT olarak nasıl temsil edebilirim?
- Bir şiiri NFT olarak temsil etmek için, sözleşmeniz içinde her şiire benzersiz bir ID atayan ve bu ID'yi belirli bir URI (örneğin şiirin içeriğinin veya metadata'sının depolandığı bir IPFS bağlantısı) ile ilişkilendiren bir mekanizma oluşturmanız gerekir. Bu yapı, ERC-721 veya ERC-1155 gibi NFT standartlarına benzer bir şekilde çalışır ve şiirin dijital sahipliğini blok zinciri üzerinde ispatlamanızı sağlar.