Solana'yı Borsanıza Ekleyin

Bu kılavuz, Solana'nın yerel token'ı SOL'u kripto para borsanıza nasıl ekleyeceğinizi açıklamaktadır.

Node Kurulumu

Yüksek kaliteli bilgisayarlara/bulut örneklerine en az iki node kurmanızı, yeni sürümlere zamanında güncelleme yapmanızı ve hizmet operasyonlarını yerleşik bir izleme aracıyla takip etmenizi şiddetle öneririz.

Bu kurulum size şunları sağlar:

  • veri almak ve para çekme işlemlerini göndermek için Solana mainnet kümesine kendi yönettiğiniz bir ağ geçidine sahip olmak
  • ne kadar geçmiş blok verisinin saklanacağı üzerinde tam kontrol sahibi olmak
  • bir node arızalansa bile hizmet erişilebilirliğinizi sürdürmek

Solana node'ları, hızlı bloklarımızı ve yüksek TPS'yi işleyebilmek için görece yüksek hesaplama gücü gerektirir. Spesifik gereksinimler için lütfen donanım önerilerine bakın.

Bir api node'u çalıştırmak için:

  1. Solana komut satırı araç paketini yükleyin
  2. validator'ı en az aşağıdaki parametrelerle başlatın:
solana-validator \
--ledger <LEDGER_PATH> \
--identity <VALIDATOR_IDENTITY_KEYPAIR> \
--entrypoint <CLUSTER_ENTRYPOINT> \
--expected-genesis-hash <EXPECTED_GENESIS_HASH> \
--rpc-port 8899 \
--no-voting \
--enable-rpc-transaction-history \
--limit-ledger-size \
--known-validator <VALIDATOR_ADDRESS> \
--only-known-rpc

--ledger seçeneğini istediğiniz defter depolama konumuna, --rpc-port seçeneğini ise açmak istediğiniz porta göre özelleştirin.

--entrypoint ve --expected-genesis-hash parametreleri, katıldığınız kümeye özgüdür. Mainnet için güncel parametreler

--limit-ledger-size parametresi, node'unuzun diskte kaç defter shred'i saklayacağını belirlemenize olanak tanır. Bu parametreyi eklemezseniz validator, disk alanı tükenene kadar tüm defteri saklar. Varsayılan değer, defter disk kullanımını 500 GB'ın altında tutmaya çalışır. İstenirse --limit-ledger-size seçeneğine bir bağımsız değişken ekleyerek daha fazla veya daha az disk kullanımı talep edilebilir. --limit-ledger-size tarafından kullanılan varsayılan sınır değeri için solana-validator --help komutunu çalıştırın. Özel bir sınır değeri seçimi hakkında daha fazla bilgiye buradan ulaşabilirsiniz.

Bir veya daha fazla --known-validator parametresi belirtmek, sizi kötü amaçlı bir anlık görüntüden önyüklemeye karşı koruyabilir. Bilinen validator'larla önyüklemenin değeri hakkında daha fazla bilgi

Değerlendirmeye alınabilecek isteğe bağlı parametreler:

  • --private-rpc, RPC portunuzun diğer node'lar tarafından kullanılmak üzere yayımlanmasını engeller
  • --rpc-bind-address, RPC portunu bağlamak için farklı bir IP adresi belirlemenize olanak tanır

Otomatik Yeniden Başlatma ve İzleme

Mümkün olduğunca az veri kaybetmek için her node'unuzu çıkışta otomatik olarak yeniden başlayacak şekilde yapılandırmanızı öneririz. Solana yazılımını bir systemd servisi olarak çalıştırmak bunun için mükemmel bir seçenektir.

İzleme amacıyla, validator'ınızı izleyebilen ve solana-validator sürecinin sağlıksız olduğunu tespit edebilen solana-watchtower aracını sunuyoruz. Bu araç, Slack, Telegram, Discord veya Twilio üzerinden sizi uyaracak şekilde doğrudan yapılandırılabilir. Ayrıntılar için solana-watchtower --help komutunu çalıştırın.

solana-watchtower --validator-identity <YOUR VALIDATOR IDENTITY>

Şu konular hakkında daha fazla bilgi edinebilirsiniz: Solana Watchtower için en iyi uygulamalar bu dokümanlarda.

Yeni Yazılım Sürüm Duyuruları

Sık sık yeni yazılım yayınlıyoruz (haftada yaklaşık 1 sürüm). Bazen yeni sürümler uyumsuz protokol değişiklikleri içerir; bu durum, blok işlemede hata oluşmaması için yazılımın zamanında güncellenmesini zorunlu kılar.

Her türlü sürüm (normal ve güvenlik) için resmi sürüm duyurularımız, #mb-announcement adlı bir discord kanalı aracılığıyla iletilmektedir (mb, mainnet-beta anlamına gelir).

Stake edilmiş validator'larda olduğu gibi, borsa tarafından işletilen validator'ların da normal bir sürüm duyurusunun ardından bir ila iki iş günü içinde güncellenmesini bekliyoruz. Güvenlikle ilgili sürümler için daha acil önlem alınması gerekebilir.

Defter Sürekliliği

Varsayılan olarak, her node'unuz bilinen validator'larınızdan biri tarafından sağlanan bir anlık görüntüden önyükleme yapar. Bu anlık görüntü zincirin mevcut durumunu yansıtır; ancak eksiksiz geçmiş defteri içermez. Node'larınızdan biri kapanıp yeni bir anlık görüntüden yeniden başlarsa o node'un defterinde bir boşluk oluşabilir. Bu sorunu önlemek için solana-validator komutunuza --no-snapshot-fetch parametresini ekleyerek anlık görüntü yerine geçmiş defter verisi alın.

İlk önyüklemenizde --no-snapshot-fetch parametresini kullanmayın; zira genesis bloğundan itibaren node'u tamamen önyüklemek mümkün değildir. Bunun yerine önce bir anlık görüntüden önyükleme yapın, ardından yeniden başlatmalar için --no-snapshot-fetch parametresini ekleyin.

Ağın geri kalanından node'larınıza sunulan geçmiş defter miktarının her an sınırlı olduğunu göz önünde bulundurmak önemlidir. Çalışır durumdayken validator'larınız önemli bir kesinti yaşarsa ağa yetişemeyebilir ve bilinen bir validator'dan yeni bir anlık görüntü indirmeleri gerekebilir. Bu durumda validator'larınızın geçmiş defter verilerinde doldurulamayan bir boşluk oluşacaktır.

Validator Port Maruziyetini En Aza İndirme

validator, tüm diğer Solana validator'larından gelen gelen trafik için çeşitli UDP ve TCP portlarının açık olmasını gerektirir. Bu en verimli çalışma modu olup güçlü şekilde önerilse de validator'ı yalnızca tek bir Solana validator'ından gelen gelen trafiği gerektirecek şekilde kısıtlamak mümkündür.

Önce --restricted-repair-only-mode bağımsız değişkenini ekleyin. Bu, validator'ın kısıtlı modda çalışmasına neden olur; bu modda validator diğer validator'lardan push almaz, bunun yerine bloklar için diğer validator'ları sürekli olarak sorgulamak zorunda kalır. validator yalnızca Gossip ve ServeR ("serve repair") portlarını kullanarak diğer validator'lara UDP paketleri iletir ve yalnızca kendi Gossip ile Repair portlarında UDP paketleri alır.

Gossip portu çift yönlüdür ve validator'ınızın kümenin geri kalanıyla iletişimde kalmasını sağlar. Turbine artık devre dışı olduğundan validator'ınız, ağın geri kalanından yeni bloklar almak üzere onarım istekleri göndermek için ServeR üzerinden iletim yapar. validator'ınız ardından diğer validator'lardan Repair portu üzerinden onarım yanıtları alır.

validator'ı yalnızca bir veya daha fazla validator'dan blok isteyecek şekilde daha da kısıtlamak için önce hedef validator'ın kimlik pubkey'ini belirleyin ve her PUBKEY için --gossip-pull-validator PUBKEY --repair-validator PUBKEY bağımsız değişkenlerini ekleyin. Bu işlem, eklediğiniz her validator üzerinde kaynak tüketimine yol açacağından lütfen bunu yalnızca gerektiğinde ve hedef validator ile görüştükten sonra yapın.

validator'ınız artık yalnızca açıkça listelenen validator'larla ve yalnızca Gossip, Repair ve ServeR portları üzerinden iletişim kuruyor olmalıdır.

Yatırım Hesaplarının Kurulumu

Solana hesapları zincir üzerinde herhangi bir başlatma gerektirmez; bir miktar SOL içerdiklerinde otomatik olarak var olurlar. Borsanız için bir yatırım hesabı oluşturmak üzere cüzdan araçlarımızdan herhangi birini kullanarak bir Solana keypair oluşturmanız yeterlidir.

Her kullanıcınız için benzersiz bir yatırım hesabı kullanmanızı öneririz.

Solana hesaplarının rent'ten muaf olabilmesi için SOL cinsinden 2 yıllık rent miktarı kadar bakiye içermesi gerekir. Yatırım hesaplarınız için minimum rent muafiyeti bakiyesini öğrenmek üzere getMinimumBalanceForRentExemption endpoint'ini sorgulayın:

curl https://api.devnet.solana.com -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getMinimumBalanceForRentExemption",
"params": [0]
}'
Sonuç
{ "jsonrpc": "2.0", "result": 890880, "id": 1 }

Çevrimdışı Hesaplar

Daha fazla güvenlik için bir veya daha fazla tahsilat hesabının anahtarlarını çevrimdışı tutmak isteyebilirsiniz. Bu durumda, çevrimdışı yöntemlerimizi kullanarak SOL'u sıcak hesaplara taşımanız gerekecektir.

Yatırımları Dinleme

Bir kullanıcı borsanıza SOL yatırmak istediğinde, uygun yatırım adresine transfer göndermesini isteyin.

Versiyonlanmış İşlem Migrasyonu

Mainnet ağı versiyonlanmış işlemleri işlemeye başladığında, borsaların değişiklik yapması ZORUNLUDUR. Herhangi bir değişiklik yapılmazsa, versiyonlanmış bir işlemin veya versiyonlanmış işlemler içeren bir bloğun getirilmesi hata döndüreceğinden yatırım tespiti artık düzgün çalışmayacaktır.

  • {"maxSupportedTransactionVersion": 0}

    Yatırım tespitinin kesintisiz çalışması için maxSupportedTransactionVersion parametresinin getBlock ve getTransaction isteklerine eklenmesi zorunludur. En güncel işlem versiyonu 0'dır ve desteklenen maksimum işlem versiyonu değeri olarak belirtilmelidir.

Versiyonlanmış işlemlerin, kullanıcıların zincir üzerindeki adres arama tablolarından yüklenen başka bir hesap anahtarları kümesi kullanan işlemler oluşturmasına olanak tanıdığını anlamak önemlidir.

  • {"encoding": "jsonParsed"}

    Bloklar ve işlemler getirilirken artık "jsonParsed" encoding kullanılması önerilmektedir; çünkü bu yöntem, tüm işlem hesabı anahtarlarını (arama tablolarındakileri de dahil olmak üzere) mesajın "accountKeys" listesine ekler. Bu sayede preBalances / postBalances ve preTokenBalances / postTokenBalances alanlarında belirtilen bakiye değişikliklerini çözmek kolaylaşır.

    "json" encoding kullanıldığında ise preBalances / postBalances ve preTokenBalances / postTokenBalances içindeki girdiler, "accountKeys" listesinde YER ALMAYAN ve işlem meta verilerindeki "loadedAddresses" girdileri kullanılarak çözülmesi gereken hesap anahtarlarına başvurabilir.

Bloklar İçin Yoklama

Borsanızdaki tüm yatırım hesaplarını takip etmek için Solana API node'unuzun JSON-RPC servisini kullanarak onaylanan her bloğu yoklayın ve ilgilendiğiniz adresleri inceleyin.

  • Hangi blokların mevcut olduğunu belirlemek için, halihazırda işlediğiniz son bloğu start-slot parametresi olarak geçirerek bir getBlocks isteği gönderin:
curl https://api.devnet.solana.com -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlocks",
"params": [160017005, 160017015]
}'
Sonuç
{
"jsonrpc": "2.0",
"result": [
160017005, 160017006, 160017007, 160017012, 160017013, 160017014, 160017015
],
"id": 1
}

Her slot bir blok üretmez, dolayısıyla tam sayı dizisinde boşluklar olabilir.

  • Her blok için içeriğini bir getBlock isteğiyle talep edin:

Blok Getirme İpuçları

  • {"rewards": false}

Varsayılan olarak, getirilen bloklar her blok için validator ücretleri ve epoch sınırlarındaki staking ödülleri hakkında bilgi döndürür. Bu bilgiye ihtiyaç duymuyorsanız "rewards" parametresiyle devre dışı bırakın.

  • {"transactionDetails": "accounts"}

Varsayılan olarak, getirilen bloklar hesap bakiyelerini takip etmek için gerekli olmayan çok sayıda işlem bilgisi ve meta verisi döndürür. Blok getirme işlemini hızlandırmak için "transactionDetails" parametresini ayarlayın.

curl https://api.devnet.solana.com -X POST -H 'Content-Type: application/json' -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlock",
"params": [
166974442,
{
"encoding": "jsonParsed",
"maxSupportedTransactionVersion": 0,
"transactionDetails": "accounts",
"rewards": false
}
]
}'
Sonuç
{
"jsonrpc": "2.0",
"result": {
"blockHeight": 157201607,
"blockTime": 1665070281,
"blockhash": "HKhao674uvFc4wMK1Cm3UyuuGbKExdgPFjXQ5xtvsG3o",
"parentSlot": 166974441,
"previousBlockhash": "98CNLU4rsYa2HDUyp7PubU4DhwYJJhSX9v6pvE7SWsAo",
"transactions": [
... (omit)
{
"meta": {
"err": null,
"fee": 5000,
"postBalances": [
1110663066,
1,
1040000000
],
"postTokenBalances": [],
"preBalances": [
1120668066,
1,
1030000000
],
"preTokenBalances": [],
"status": {
"Ok": null
}
},
"transaction": {
"accountKeys": [
{
"pubkey": "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde",
"signer": true,
"source": "transaction",
"writable": true
},
{
"pubkey": "11111111111111111111111111111111",
"signer": false,
"source": "transaction",
"writable": false
},
{
"pubkey": "G1wZ113tiUHdSpQEBcid8n1x8BAvcWZoZgxPKxgE5B7o",
"signer": false,
"source": "lookupTable",
"writable": true
}
],
"signatures": [
"2CxNRsyRT7y88GBwvAB3hRg8wijMSZh3VNYXAdUesGSyvbRJbRR2q9G1KSEpQENmXHmmMLHiXumw4dp8CvzQMjrM"
]
},
"version": 0
},
... (omit)
]
},
"id": 1
}

preBalances ve postBalances alanları, tüm işlemi ayrıştırmak zorunda kalmadan her hesaptaki bakiye değişikliklerini takip etmenizi sağlar. Bu alanlar, accountKeys listesiyle indekslenmiş olarak her hesabın başlangıç ve bitiş bakiyelerini lamport cinsinden listeler. Örneğin, ilgilenilen para yatırma adresi G1wZ113tiUHdSpQEBcid8n1x8BAvcWZoZgxPKxgE5B7o ise, bu işlem 1040000000 - 1030000000 = 10.000.000 lamport = 0,01 SOL tutarında bir transfer temsil etmektedir.

İşlem türü veya diğer ayrıntılar hakkında daha fazla bilgiye ihtiyaç duyarsanız, bloğu RPC'den ikili biçimde talep edebilir ve Rust SDK ya da Javascript SDK'mızdan birini kullanarak ayrıştırabilirsiniz.

Adres Geçmişi

Belirli bir adresin işlem geçmişini de sorgulayabilirsiniz. Bu yöntem genellikle tüm para yatırma adreslerinizi tüm slot'lar boyunca takip etmek için uygun bir yöntem değildir; ancak belirli bir zaman dilimi için birkaç hesabı incelemek amacıyla yararlı olabilir.

curl https://api.devnet.solana.com -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSignaturesForAddress",
"params": [
"3M2b3tLji7rvscqrLAHMukYxDK2nB96Q9hwfV6QkdzBN",
{
"limit": 3
}
]
}'
Sonuç
{
"jsonrpc": "2.0",
"result": [
{
"blockTime": 1662064640,
"confirmationStatus": "finalized",
"err": null,
"memo": null,
"signature": "3EDRvnD5TbbMS2mCusop6oyHLD8CgnjncaYQd5RXpgnjYUXRCYwiNPmXb6ZG5KdTK4zAaygEhfdLoP7TDzwKBVQp",
"slot": 148697216
},
{
"blockTime": 1662064434,
"confirmationStatus": "finalized",
"err": null,
"memo": null,
"signature": "4rPQ5wthgSP1kLdLqcRgQnkYkPAZqjv5vm59LijrQDSKuL2HLmZHoHjdSLDXXWFwWdaKXUuryRBGwEvSxn3TQckY",
"slot": 148696843
},
{
"blockTime": 1662064341,
"confirmationStatus": "finalized",
"err": null,
"memo": null,
"signature": "36Q383JMiqiobuPV9qBqy41xjMsVnQBm9rdZSdpbrLTGhSQDTGZJnocM4TQTVfUGfV2vEX9ZB3sex6wUBUWzjEvs",
"slot": 148696677
}
],
"id": 1
}
  • Döndürülen her imza için, bir getTransaction isteği göndererek işlem ayrıntılarını alın:
curl https://api.devnet.solana.com -X POST -H 'Content-Type: application/json' -d '{
"jsonrpc":"2.0",
"id":1,
"method":"getTransaction",
"params":[
"2CxNRsyRT7y88GBwvAB3hRg8wijMSZh3VNYXAdUesGSyvbRJbRR2q9G1KSEpQENmXHmmMLHiXumw4dp8CvzQMjrM",
{
"encoding":"jsonParsed",
"maxSupportedTransactionVersion":0
}
]
}'
Sonuç
{
"jsonrpc": "2.0",
"result": {
"blockTime": 1665070281,
"meta": {
"err": null,
"fee": 5000,
"innerInstructions": [],
"logMessages": [
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"postBalances": [1110663066, 1, 1040000000],
"postTokenBalances": [],
"preBalances": [1120668066, 1, 1030000000],
"preTokenBalances": [],
"rewards": [],
"status": {
"Ok": null
}
},
"slot": 166974442,
"transaction": {
"message": {
"accountKeys": [
{
"pubkey": "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde",
"signer": true,
"source": "transaction",
"writable": true
},
{
"pubkey": "11111111111111111111111111111111",
"signer": false,
"source": "transaction",
"writable": false
},
{
"pubkey": "G1wZ113tiUHdSpQEBcid8n1x8BAvcWZoZgxPKxgE5B7o",
"signer": false,
"source": "lookupTable",
"writable": true
}
],
"addressTableLookups": [
{
"accountKey": "4syr5pBaboZy4cZyF6sys82uGD7jEvoAP2ZMaoich4fZ",
"readonlyIndexes": [],
"writableIndexes": [3]
}
],
"instructions": [
{
"parsed": {
"info": {
"destination": "G1wZ113tiUHdSpQEBcid8n1x8BAvcWZoZgxPKxgE5B7o",
"lamports": 10000000,
"source": "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde"
},
"type": "transfer"
},
"program": "system",
"programId": "11111111111111111111111111111111"
}
],
"recentBlockhash": "BhhivDNgoy4L5tLtHb1s3TP19uUXqKiy4FfUR34d93eT"
},
"signatures": [
"2CxNRsyRT7y88GBwvAB3hRg8wijMSZh3VNYXAdUesGSyvbRJbRR2q9G1KSEpQENmXHmmMLHiXumw4dp8CvzQMjrM"
]
},
"version": 0
},
"id": 1
}

Para Çekme İşlemleri Gönderme

Kullanıcının SOL çekme talebini karşılamak için bir Solana transfer işlemi oluşturmanız ve bunu kümenize iletilmek üzere API düğümüne göndermeniz gerekmektedir.

Senkron

Solana kümesine senkron bir transfer göndermek, bir transferin başarılı olduğunu ve küme tarafından sonuçlandırıldığını kolayca doğrulamanızı sağlar.

Solana'nın komut satırı aracı, transfer işlemlerini oluşturmak, göndermek ve onaylamak için basit bir komut sunar: solana transfer. Bu yöntem varsayılan olarak, işlem küme tarafından sonuçlandırılana kadar bekler ve stderr üzerinde ilerlemeyi takip eder. İşlem başarısız olursa, işlem hatalarını raporlar.

solana transfer <USER_ADDRESS> <AMOUNT> --allow-unfunded-recipient --keypair <KEYPAIR> --url http://localhost:8899

Solana Javascript SDK, JS ekosistemi için benzer bir yaklaşım sunar. Bir transfer işlemi oluşturmak için SystemProgram'ı kullanın ve sendAndConfirmTransaction yöntemiyle gönderin.

Asenkron

Daha fazla esneklik için para çekme transferlerini asenkron olarak gönderebilirsiniz. Bu durumlarda, işlemin başarılı olduğunu ve küme tarafından sonuçlandırıldığını doğrulamak sizin sorumluluğunuzdadır.

Not: Her işlem, etkinliğini belirtmek için bir son blockhash içerir. Onaylanmamış veya küme tarafından sonuçlandırılmamış gibi görünen bir para çekme transferini yeniden denemeden önce bu blockhash'in süresinin dolmasını beklemeniz kritik öneme sahiptir. Aksi takdirde çift harcama riskiyle karşılaşabilirsiniz. Daha fazla bilgi için aşağıdaki blockhash sona erme bölümüne bakın.

Önce, getFees uç noktasını veya CLI komutunu kullanarak son bir blockhash alın:

solana fees --url http://localhost:8899

Komut satırı aracında, bir transferi asenkron olarak göndermek için --no-wait bağımsız değişkenini geçirin ve --blockhash bağımsız değişkeniyle son blockhash'inizi ekleyin:

solana transfer <USER_ADDRESS> <AMOUNT> --no-wait --allow-unfunded-recipient --blockhash <RECENT_BLOCKHASH> --keypair <KEYPAIR> --url http://localhost:8899

Ayrıca işlemi manuel olarak oluşturabilir, imzalayabilir ve serileştirebilir; ardından JSON-RPC sendTransaction uç noktasını kullanarak kümeye gönderebilirsiniz.

İşlem Onayları ve Kesinleşme

getSignatureStatuses JSON-RPC uç noktasını kullanarak bir işlem grubunun durumunu alın. confirmations alanı, işlem işlendikten bu yana kaç adet onaylanmış blok geçtiğini raporlar. confirmations: null ise işlem kesinleşmiş demektir.

curl https://api.devnet.solana.com -X POST -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0",
"id":1,
"method":"getSignatureStatuses",
"params":[
[
"4cdd1oX7cfVALfr26tP52BZ6cSzrgnNGtYD7BFhm6FFeZV5sPTnRvg6NRn8yC6DbEikXcrNChBM5vVJnTgKhGhVu",
"5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"
]
]
}'
Sonuç
{
"jsonrpc": "2.0",
"result": {
"context": {
"slot": 82
},
"value": [
{
"slot": 72,
"confirmations": 10,
"err": null,
"status": {
"Ok": null
}
},
{
"slot": 48,
"confirmations": null,
"err": null,
"status": {
"Ok": null
}
}
]
},
"id": 1
}

Blockhash Sona Ermesi

Belirli bir blockhash'in hâlâ geçerli olup olmadığını, blockhash'i parametre olarak ileterek bir getFeeCalculatorForBlockhash isteği göndererek kontrol edebilirsiniz. Yanıt değeri null ise blockhash süresi dolmuş demektir ve söz konusu blockhash'i kullanan para çekme işlemi hiçbir zaman başarılı olamaz.

Para Çekme İşlemleri İçin Kullanıcı Tarafından Sağlanan Hesap Adreslerinin Doğrulanması

Para çekme işlemleri geri alınamaz olduğundan, kullanıcı fonlarının yanlışlıkla kaybolmasını önlemek amacıyla para çekme işlemini yetkilendirmeden önce kullanıcı tarafından sağlanan hesap adresini doğrulamak iyi bir uygulama olabilir.

Temel Doğrulama

Solana adresleri, bitcoin base58 alfabesiyle kodlanmış 32 baytlık bir dizidir. Bu, aşağıdaki düzenli ifadeyle eşleşen bir ASCII metin dizesiyle sonuçlanır:

[1-9A-HJ-NP-Za-km-z]{32,44}

Bu kontrol tek başına yetersizdir; zira Solana adresleri sağlama toplamı içermez, dolayısıyla yazım hataları tespit edilemez. Kullanıcı girişini daha fazla doğrulamak için dize çözümlenebilir ve elde edilen bayt dizisinin uzunluğunun 32 olduğu doğrulanabilir. Ancak, tek bir eksik karakter, ters çevrilmiş karakterler veya büyük/küçük harf görmezden gelme gibi yazım hatalarına rağmen 32 bayta çözümlenebilen bazı adresler de mevcuttur.

Gelişmiş Doğrulama

Yukarıda açıklanan yazım hatası açığı nedeniyle, aday para çekme adresleri için bakiye sorgulanması ve sıfır olmayan bir bakiye keşfedildiğinde kullanıcının niyetini onaylaması istenmesi tavsiye edilir.

Geçerli ed25519 pubkey Kontrolü

Solana'daki normal bir hesabın adresi, 256 bitlik bir ed25519 genel anahtarının Base58 ile kodlanmış bir dizesidir. Tüm bit desenleri ed25519 eğrisi için geçerli genel anahtarlar değildir; bu nedenle kullanıcı tarafından sağlanan hesap adreslerinin en azından geçerli ed25519 genel anahtarları olduğundan emin olmak mümkündür.

Java

Kullanıcı tarafından sağlanan bir adresi geçerli bir ed25519 genel anahtarı olarak doğrulamaya ilişkin bir Java örneği aşağıda verilmiştir:

Aşağıdaki kod örneği, Maven kullandığınızı varsaymaktadır.

pom.xml:

<repositories>
...
<repository>
<id>spring</id>
<url>https://repo.spring.io/libs-release/</url>
</repository>
</repositories>
...
<dependencies>
...
<dependency>
<groupId>io.github.novacrypto</groupId>
<artifactId>Base58</artifactId>
<version>0.1.3</version>
</dependency>
<dependency>
<groupId>cafe.cryptography</groupId>
<artifactId>curve25519-elisabeth</artifactId>
<version>0.1.0</version>
</dependency>
<dependencies>
import io.github.novacrypto.base58.Base58;
import cafe.cryptography.curve25519.CompressedEdwardsY;
public class PubkeyValidator
{
public static boolean verifyPubkey(String userProvidedPubkey)
{
try {
return _verifyPubkeyInternal(userProvidedPubkey);
} catch (Exception e) {
return false;
}
}
public static boolean _verifyPubkeyInternal(String maybePubkey) throws Exception
{
byte[] bytes = Base58.base58Decode(maybePubkey);
return !(new CompressedEdwardsY(bytes)).decompress().isSmallOrder();
}
}

Minimum Para Yatırma ve Çekme Tutarları

SOL için her para yatırma ve çekme işlemi, cüzdan adresindeki hesabın minimum kira muafiyeti bakiyesine (veri içermeyen temel bir SOL hesabı) eşit veya bundan büyük olmalıdır. Mevcut değer: 0,000890880 SOL

Benzer şekilde, her para yatırma hesabı en az bu bakiyeyi içermelidir.

curl https://api.devnet.solana.com -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getMinimumBalanceForRentExemption",
"params": [0]
}'
Sonuç
{ "jsonrpc": "2.0", "result": 890880, "id": 1 }

Önceliklendirme Ücretleri ve Hesaplama Birimleri

Yüksek talep dönemlerinde, bir validator daha yüksek ekonomik değere sahip diğer işlemleri seçtiği için bir işlemin bloğa eklenmeden sona ermesi mümkündür. Önceliklendirme Ücretleri doğru şekilde uygulanmadığında Solana üzerindeki geçerli işlemler gecikebilir veya düşürülebilir.

Önceliklendirme Ücretleri, işlemlerin bloklar içine alınmasını güvence altına almak ve bu gibi durumlarda teslimatı sağlamaya yardımcı olmak amacıyla temel İşlem Ücretine ek olarak eklenebilen ilave ücretlerdir.

Bu öncelik ücretleri, istenen öncelik ücretini belirleyen özel bir Compute Budget talimatı eklenerek işleme dahil edilir.

Önemli Not

Bu talimatların uygulanmaması ağ kesintilerine ve işlem düşmelerine yol açabilir. Solana'yı destekleyen her borsanın kesintileri önlemek amacıyla öncelik ücretlerini kullanması şiddetle tavsiye edilir.

Önceliklendirme Ücreti Nedir?

Önceliklendirme Ücretleri, ağdaki bloklar içinde validator düğümlerinin işlemleri dahil etmesini ekonomik açıdan cazip kılmak amacıyla işlemlere eklenen mikro-lamport/Hesaplama Birimi (örneğin küçük miktarda SOL) cinsinden fiyatlandırılır.

Önceliklendirme Ücreti Ne Kadar Olmalıdır?

Önceliklendirme ücretinizi belirlemek için kullanılan yöntem, ağ için cazip olması muhtemel bir ücret belirlemek amacıyla son önceliklendirme ücretlerini sorgulamayı kapsamalıdır. getRecentPrioritizationFees RPC yöntemini kullanarak, son bir blokta işlemin yer alması için gereken önceliklendirme ücretlerini sorgulayabilirsiniz.

Bu öncelik ücretlerinin fiyatlandırma stratejisi kullanım senaryonuza göre farklılık gösterir. Bunu yapmanın standart bir yolu yoktur. Önceliklendirme Ücretlerinizi belirlemeye yönelik bir strateji; işlem başarı oranınızı hesaplamak, ardından son işlem ücretleri API'sine yönelik bir sorguya karşın Önceliklendirme Ücretinizi artırmak ve buna göre ayarlamak olabilir. Önceliklendirme Ücretleri fiyatlandırması, ağdaki aktiviteye ve diğer katılımcıların tekliflerine bağlı olarak dinamik bir yapıda olacak ve yalnızca gerçekleştikten sonra bilinebilecektir.

getRecentPrioritizationFees API çağrısının kullanımındaki zorluklardan biri, her blok için yalnızca en düşük ücreti döndürebilmesidir. Bu değer çoğunlukla sıfır olacaktır; bu da validator düğümleri tarafından reddedilmemek için kullanılacak Önceliklendirme Ücretinin tam anlamıyla yararlı bir tahmini değildir.

getRecentPrioritizationFees API, parametre olarak hesapların pubkey değerlerini alır ve ardından bu hesaplar için minimum önceliklendirme ücretlerinin en yükseğini döndürür. Hesap belirtilmediğinde API, bloğa dahil olmak için gereken en düşük ücreti döndürür; bu değer genellikle sıfırdır (blok dolu olmadıkça).

Borsalar ve uygulamalar, bir işlemin yazma kilidi uygulayacağı hesaplarla birlikte RPC uç noktasını sorgulamalıdır. RPC uç noktası max(account_1_min_fee, account_2_min_fee, ... account_n_min_fee) değerini döndürür; bu değer, kullanıcının söz konusu işlem için önceliklendirme ücretini belirlemesinde temel nokta olmalıdır.

Önceliklendirme Ücretlerini belirlemeye yönelik farklı yaklaşımlar mevcuttur ve uygulanacak en iyi ücreti belirlemek için bazı üçüncü taraf API'leri bulunmaktadır. Ağın dinamik yapısı göz önüne alındığında, Önceliklendirme ücretlerinizi fiyatlandırmanın "mükemmel" bir yolu olmayacaktır; bu nedenle bir yol seçmeden önce dikkatli bir analiz yapılmalıdır.

Önceliklendirme Ücretleri Nasıl Uygulanır?

Bir işleme öncelik ücreti eklemek, söz konusu işleme iki Compute Budget talimatı önceden eklenmesinden oluşur:

  • birincisi hesaplama birimi fiyatını belirlemek için,
  • diğeri ise hesaplama birimi limitini belirlemek için

Burada, öncelik ücretlerinin nasıl kullanılacağına dair daha fazla bilgi içeren daha ayrıntılı bir geliştirici kılavuzuna da ulaşabilirsiniz.

Temel İşlem Ücretinin (5.000 Lamport) üzerine bir Önceliklendirme Ücreti eklemek için bir setComputeUnitPrice talimatı oluşturun.

// import { ComputeBudgetProgram } from "@solana/web3.js"
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: number });

Mikro-lamport cinsinden sağlanan değer, Önceliklendirme Ücretini Lamport cinsinden belirlemek için Hesaplama Birimi (HB) bütçesiyle çarpılır. Örneğin, HB bütçeniz 1M HB ise ve 1 mikroLamport/HB eklerseniz, Önceliklendirme Ücreti 1 lamport olur (1M * 0,000001). Toplam ücret ise 5001 lamport olacaktır.

İşlem için yeni bir hesaplama birimi bütçesi belirlemek amacıyla bir setComputeUnitLimit talimatı oluşturun.

// import { ComputeBudgetProgram } from "@solana/web3.js"
ComputeBudgetProgram.setComputeUnitLimit({ units: number });

units değeri, Solana çalışma ortamının varsayılan hesaplama bütçesi değerinin yerine geçecektir.

İşlem için Gereken En Düşük HB Miktarını Belirleyin

İşlemlerin verimliliği en üst düzeye çıkarmak ve genel ücretleri en aza indirmek için yürütme için gereken minimum hesaplama birimi (HB) miktarını talep etmesi gerekir.

Bir işlemin tükettiği HB miktarını, işlemi devnet gibi farklı bir Solana kümesinde göndererek öğrenebilirsiniz. Örneğin, basit bir token transferi 300 HB tüketir.

// import { ... } from "@solana/web3.js"
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
// note: set this to be the lowest actual CU consumed by the transaction
units: 300
});
const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 1
});
const transaction = new Transaction()
.add(modifyComputeUnits)
.add(addPriorityFee)
.add(
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: toAccount,
lamports: 10000000
})
);

Önceliklendirme Ücretleri ve Kalıcı Nonce'lar

Kurulumunuz Dayanıklı Nonce İşlemleri kullanıyorsa, başarılı işlemler sağlamak amacıyla Önceliklendirme Ücretlerini Dayanıklı İşlem Nonce'larıyla birlikte doğru şekilde uygulamak önemlidir. Aksi takdirde, hedeflenen Dayanıklı Nonce işlemleri bu şekilde algılanmayacaktır.

AdvanceNonceAccount talimatı, compute budget talimatları öncelik ücretlerini belirtmek için kullanılsa bile, talimat listesinde İLK olarak belirtilmek ZORUNDADIR. Bu durum Dayanıklı İşlem Nonce'ları KULLANIYOR olmanız halinde geçerlidir.

Bu geliştirici kılavuzunda dayanıklı nonce'lar ve öncelik ücretlerini birlikte kullanan belirli bir kod örneği bulabilirsiniz.

SPL Token Standardının Desteklenmesi

SPL Token, Solana blok zincirinde sarılı/sentetik token oluşturma ve değişimi için kullanılan standarttır.

SPL Token iş akışı, yerel SOL token'larınkine benzerdir; ancak bu bölümde ele alınacak birkaç farklılık mevcuttur.

Token Mint'leri

Her bir SPL Token türü, bir mint account oluşturularak tanımlanır. Bu account; arz, ondalık basamak sayısı ve mint üzerinde kontrole sahip çeşitli yetkiler gibi token özelliklerini açıklayan meta verileri depolar. Her SPL Token account, ilişkili mint'ini referans alır ve yalnızca o türdeki SPL Token'larla etkileşime girebilir.

spl-token CLI Aracının Yüklenmesi

SPL Token account'ları, spl-token komut satırı aracı kullanılarak sorgulanır ve değiştirilir. Bu bölümde verilen örnekler, aracın yerel sisteme yüklenmiş olmasını gerektirir.

spl-token, Rust cargo komut satırı aracı aracılığıyla Rust crates.io üzerinden dağıtılmaktadır. cargo'nun en son sürümü, platformunuza özel kullanışlı bir tek satırlık komutla rustup.rs adresinden yüklenebilir. cargo yüklendikten sonra spl-token aşağıdaki komutla edinilebilir:

cargo install spl-token-cli

Ardından yüklü sürümü doğrulamak için kontrol edebilirsiniz

spl-token --version

Sonuç aşağıdakine benzer bir çıktı olmalıdır

spl-token-cli 2.0.1

Account Oluşturma

SPL Token account'larının, yerel System Program account'larında bulunmayan ek gereksinimleri vardır:

  1. SPL Token account'larına token yatırılabilmesi için önce oluşturulmaları gerekir. Token account'ları, spl-token create-account komutuyla açıkça veya spl-token transfer --fund-recipient ... komutuyla örtük olarak oluşturulabilir.
  2. SPL Token account'larının var oldukları süre boyunca kira muafiyeti korunmalıdır; bu nedenle account oluşturulurken küçük bir miktarda yerel SOL token'ı yatırılması gerekmektedir. SPL Token account'ları için bu miktar 0,00203928 SOL (2.039.280 lamport)'tur.

Komut Satırı

Aşağıdaki özelliklere sahip bir SPL Token account'u oluşturmak için:

  1. Belirtilen mint ile ilişkilendirilmiş
  2. Finansman account'ının keypair'i tarafından sahiplenilen
spl-token create-account <TOKEN_MINT_ADDRESS>

Örnek

spl-token create-account AkUFCWTXb3w9nY2n6SFJvBV6VwvFUCe4KBMCcgLsa2ir

Aşağıdakine benzer bir çıktı üretir:

Creating account 6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV
Signature: 4JsqZEPra2eDTHtHpB4FMWSfk3UgcCVmkKkP7zESZeMrKmFFkDkNd91pKP3vPVVZZPiu5XxyJwS73Vi5WsZL88D7

Ya da belirli bir keypair ile SPL Token account'u oluşturmak için:

solana-keygen new -o token-account.json
spl-token create-account AkUFCWTXb3w9nY2n6SFJvBV6VwvFUCe4KBMCcgLsa2ir token-account.json

Aşağıdakine benzer bir çıktı üretir:

Creating account 6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV
Signature: 4JsqZEPra2eDTHtHpB4FMWSfk3UgcCVmkKkP7zESZeMrKmFFkDkNd91pKP3vPVVZZPiu5XxyJwS73Vi5WsZL88D7

Account Bakiyesini Kontrol Etme

Komut Satırı

spl-token balance <TOKEN_ACCOUNT_ADDRESS>

Örnek

solana balance 6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV

Aşağıdakine benzer bir çıktı üretir:

0

Token Transferleri

Transfer için kaynak account, miktarı içeren gerçek token account'tur.

Ancak alıcı adresi normal bir cüzdan account'u olabilir. Söz konusu cüzdan için belirtilen mint'e ait bir associated token account henüz mevcut değilse, --fund-recipient argümanı sağlandığı takdirde transfer bu account'u oluşturacaktır.

Komut Satırı

spl-token transfer <SENDER_ACCOUNT_ADDRESS> <AMOUNT> <RECIPIENT_WALLET_ADDRESS> --fund-recipient

Örnek

spl-token transfer 6B199xxzw3PkAm25hGJpjj3Wj3WNYNHzDAnt1tEqg5BN 1

Aşağıdakine benzer bir çıktı üretir:

6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV
Transfer 1 tokens
Sender: 6B199xxzw3PkAm25hGJpjj3Wj3WNYNHzDAnt1tEqg5BN
Recipient: 6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV
Signature: 3R6tsog17QM8KfzbcbdP4aoMfwgo6hBggJDVy7dZPVmH2xbCWjEj31JKD53NzMrf25ChFjY7Uv2dfCDq4mGFFyAj

Yatırma

Her (cüzdan, mint) çifti zincir üzerinde ayrı bir account gerektirdiğinden, bu account'ların adreslerinin Associated Token Account (ATA) şeması kullanılarak SOL yatırma cüzdanlarından türetilmesi ve yalnızca ATA adreslerinden gelen yatırmaların kabul edilmesi önerilir.

Yatırma işlemlerinin izlenmesi, yukarıda açıklanan blok yoklama yöntemini izlemelidir. Her yeni blok; kullanıcının ve borsanın token account adresleri dahil başarılı işlemler için taranmalıdır.

İşlemin meta verilerindeki preTokenBalances ve postTokenBalances alanları, etkin bakiye değişimini belirlemek için kullanılmalıdır. Bu alanlar; token mint'ini, token account sahibini (cüzdan adresi) ve işlem öncesi ile sonrasındaki token account bakiyelerini içerir.

İşlemin bir parçası olarak bir token account oluşturulursa (örneğin ilk kez token alınırken), işlem öncesinde mevcut olmadığından preTokenBalances dizisinde görünmez. Bu senaryoda, yatırma tutarları hesaplanırken başlangıç bakiyesini sıfır olarak ele almalısınız. Yeni oluşturulan account, yalnızca işlem tamamlandıktan sonraki nihai bakiyesiyle postTokenBalances dizisinde görünecektir.

Örnek 1: Tek Token Transferi

Aşağıdaki işlem detayı, tek bir token transfer talimatı içeren bir işlem örneğini göstermektedir.

İşlem, token'ın 100 temel birimini (mint ondalığı için düzeltilmemiş) transfer eder ve aşağıdaki account'ları içerir:

  • Gönderen (sahip): 4fvXFPXSL9i7VbiRzoizuW4bhn1dMvRgQzQ6VevssYxw
  • Gönderen Token Account'u: 6zhjktfYBRUp7fgXLoWU7GFFrCuZa9iQkYTQzDzuvsAS
  • Alıcı Token Account'u: G5nNekUhhWFqJAiCMpKHootZ5Bfa7MXwuQ5vvKcvuxKM
  • Token Extension Program ID'si: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Alıcı (sahip) account'unun ve mint account'unun bir token transfer talimatında gerekli olmadığına dikkat edin. Referans olması açısından, ayrıştırılmış işlem meta verilerinde adreslerin yer alması nedeniyle burada listelenmiştir.

  • Alıcı (sahip): 8fjS2shNWY8xniiEMLNk1Aek4MAu8Qp2LCXJckVwTD4n
  • Mint: Fx1JZFeYbCxLrMv7422YSxpr7YzcsAgpU1MkjZTyCKi2
Transaction Metadata
{
"blockTime": "1741240211",
"meta": {
"computeUnitsConsumed": "1551",
"err": null,
"fee": "5000",
"innerInstructions": [],
"loadedAddresses": {
"readonly": [],
"writable": []
},
"logMessages": [
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [1]",
"Program log: Instruction: Transfer",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1551 of 200000 compute units",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success"
],
"postBalances": ["994375240", "2074080", "2074080", "1141440"],
"postTokenBalances": [
{
"accountIndex": 1,
"mint": "Fx1JZFeYbCxLrMv7422YSxpr7YzcsAgpU1MkjZTyCKi2",
"owner": "4fvXFPXSL9i7VbiRzoizuW4bhn1dMvRgQzQ6VevssYxw",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "0",
"decimals": 2,
"uiAmount": null,
"uiAmountString": "0"
}
},
{
"accountIndex": 2,
"mint": "Fx1JZFeYbCxLrMv7422YSxpr7YzcsAgpU1MkjZTyCKi2",
"owner": "8fjS2shNWY8xniiEMLNk1Aek4MAu8Qp2LCXJckVwTD4n",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "100",
"decimals": 2,
"uiAmount": "1",
"uiAmountString": "1"
}
}
],
"preBalances": ["994380240", "2074080", "2074080", "1141440"],
"preTokenBalances": [
{
"accountIndex": 1,
"mint": "Fx1JZFeYbCxLrMv7422YSxpr7YzcsAgpU1MkjZTyCKi2",
"owner": "4fvXFPXSL9i7VbiRzoizuW4bhn1dMvRgQzQ6VevssYxw",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "100",
"decimals": 2,
"uiAmount": "1",
"uiAmountString": "1"
}
},
{
"accountIndex": 2,
"mint": "Fx1JZFeYbCxLrMv7422YSxpr7YzcsAgpU1MkjZTyCKi2",
"owner": "8fjS2shNWY8xniiEMLNk1Aek4MAu8Qp2LCXJckVwTD4n",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "0",
"decimals": 2,
"uiAmount": null,
"uiAmountString": "0"
}
}
],
"rewards": [],
"status": {
"Ok": null
}
},
"slot": "3916",
"transaction": {
"message": {
"accountKeys": [
"4fvXFPXSL9i7VbiRzoizuW4bhn1dMvRgQzQ6VevssYxw",
"6zhjktfYBRUp7fgXLoWU7GFFrCuZa9iQkYTQzDzuvsAS",
"G5nNekUhhWFqJAiCMpKHootZ5Bfa7MXwuQ5vvKcvuxKM",
"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
],
"addressTableLookups": [],
"header": {
"numReadonlySignedAccounts": 0,
"numReadonlyUnsignedAccounts": 1,
"numRequiredSignatures": 1
},
"instructions": [
{
"accounts": [1, 2, 0],
"data": "3WBgs5fm8oDy",
"programIdIndex": 3,
"stackHeight": null
}
],
"recentBlockhash": "8soh8j2dkEniZW6Jpx9cJaWtnvrGoGUqpbaUVwUkX5R3"
},
"signatures": [
"3vr6Gj3GnBQmsZW1TtBJ3hvfFMi3h9BxLs2oaZkV41LRWeGWPVmeo16JTN8MdP3ypU5VgWAziYUjybhyZoisryQ6"
]
},
"version": "0"
}

Örnek 2: Token Account Oluşturma ve Transfer

Aşağıdaki işlem detayı, alıcı token account'unun bir token transferiyle aynı işlemde oluşturulduğu bir işlem örneğini göstermektedir.

preTokenBalances dizisinin, işlem öncesinde mevcut olmadığı için alıcı token account'unu içermediğine dikkat edin. Alıcı token account'u yalnızca işlem tamamlandıktan sonraki nihai bakiyesiyle postTokenBalances dizisinde görünür.

Transaction Metadata
{
"blockTime": "1740541705",
"meta": {
"computeUnitsConsumed": "17416",
"err": null,
"fee": "5000",
"innerInstructions": [
{
"index": 0,
"instructions": [
{
"accounts": [4],
"data": "84eT",
"programIdIndex": 7,
"stackHeight": 2
},
{
"accounts": [0, 1],
"data": "11119ExAoTptm6xKUTUcw2V69MKmyEdDmRins3j3bK43o9nHeiYUtSiaT9pc292PhNQvxj",
"programIdIndex": 3,
"stackHeight": 2
},
{
"accounts": [1],
"data": "P",
"programIdIndex": 7,
"stackHeight": 2
},
{
"accounts": [1, 4],
"data": "6b8ZSccu4ezujyhGG8KNmg75iCWbQRyjxeSfi38u8ED8N",
"programIdIndex": 7,
"stackHeight": 2
}
]
}
],
"loadedAddresses": {
"readonly": [],
"writable": []
},
"logMessages": [
"Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [1]",
"Program log: Create",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]",
"Program log: Instruction: GetAccountDataSize",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 928 of 394613 compute units",
"Program return: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb qgAAAAAAAAA=",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success",
"Program 11111111111111111111111111111111 invoke [2]",
"Program 11111111111111111111111111111111 success",
"Program log: Initialize the associated token account",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]",
"Program log: Instruction: InitializeImmutableOwner",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 487 of 388755 compute units",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]",
"Program log: Instruction: InitializeAccount3",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1440 of 385879 compute units",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success",
"Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 15865 of 400000 compute units",
"Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [1]",
"Program log: Instruction: Transfer",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1551 of 384135 compute units",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success"
],
"postBalances": [
"994375240",
"2074080",
"2074080",
"1",
"1461600",
"731913600",
"0",
"1141440"
],
"postTokenBalances": [
{
"accountIndex": 1,
"mint": "3RPRXBsdwyHhs2UnTWXoHp6Frwv4eWEbA55qCzbs9nxK",
"owner": "EzrmgRNGN9duiDAk3ABSC8eKhd1b2EUFwXVrYZDJw4hQ",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "100",
"decimals": 2,
"uiAmount": "1",
"uiAmountString": "1"
}
},
{
"accountIndex": 2,
"mint": "3RPRXBsdwyHhs2UnTWXoHp6Frwv4eWEbA55qCzbs9nxK",
"owner": "CbJNxBnU9ZWnQq12aVHhbze9nubbDVDV5rYVEqz9qFaS",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "0",
"decimals": 2,
"uiAmount": null,
"uiAmountString": "0"
}
}
],
"preBalances": [
"996454320",
"0",
"2074080",
"1",
"1461600",
"731913600",
"0",
"1141440"
],
"preTokenBalances": [
{
"accountIndex": 2,
"mint": "3RPRXBsdwyHhs2UnTWXoHp6Frwv4eWEbA55qCzbs9nxK",
"owner": "CbJNxBnU9ZWnQq12aVHhbze9nubbDVDV5rYVEqz9qFaS",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "100",
"decimals": 2,
"uiAmount": "1",
"uiAmountString": "1"
}
}
],
"rewards": [],
"status": {
"Ok": null
}
},
"slot": "81051",
"transaction": {
"message": {
"accountKeys": [
"CbJNxBnU9ZWnQq12aVHhbze9nubbDVDV5rYVEqz9qFaS",
"571u96hRRmxbRCTmp5oqC5WpJfvZhPaSXEbihLVCR5wQ",
"8y8KjtZN9tyGeAeKwr8doSpbBVVgfsZMtMjCGUDH7mmU",
"11111111111111111111111111111111",
"3RPRXBsdwyHhs2UnTWXoHp6Frwv4eWEbA55qCzbs9nxK",
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
"EzrmgRNGN9duiDAk3ABSC8eKhd1b2EUFwXVrYZDJw4hQ",
"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
],
"addressTableLookups": [],
"header": {
"numReadonlySignedAccounts": 0,
"numReadonlyUnsignedAccounts": 5,
"numRequiredSignatures": 1
},
"instructions": [
{
"accounts": [0, 1, 6, 4, 3, 7],
"data": "1",
"programIdIndex": 5,
"stackHeight": null
},
{
"accounts": [2, 1, 0],
"data": "3WBgs5fm8oDy",
"programIdIndex": 7,
"stackHeight": null
}
],
"recentBlockhash": "77QC38Q2hKFYZzUXk8JWmsAqGNKhw4k2Lm2XVUme9uqP"
},
"signatures": [
"4kuGhMeZxBHgEtej4Uv4n2arhe3jqT2GdTDPFri4JLFXYgcAtbeeXdBdzvG98HENe1tZSZqyFkm3SEvB6CfCMaM9"
]
},
"version": "0"
}

Örnek 3: Token Account Sahibini Değiştirme

Aşağıdaki işlem detayı, token account'unun owner alanının değiştirildiği bir işlem örneğini göstermektedir.

Yatırma yöntemi olarak token account'larının sahipliğinin devredilmesine (owner alanının değiştirilmesi yoluyla) izin vermek kesinlikle tavsiye edilmez.

Bunu bir yatırma yöntemi olarak desteklemeyi tercih ederseniz, postTokenBalances içindeki yeni owner alanının borsanızın kontrol ettiği ve özel anahtarına sahip olduğu bir cüzdan adresiyle eşleştiğini doğrulamalısınız.

Bir yatırımcı, token account'unun owner'ını cüzdan olmayan bir adresle (örneğin başka bir token account adresi) değiştirirse, fonlar kalıcı olarak erişilemez hale gelebilir.

Transaction Metadata
{
"blockTime": "1740598556",
"meta": {
"computeUnitsConsumed": "1167",
"err": null,
"fee": "5000",
"innerInstructions": [],
"loadedAddresses": {
"readonly": [],
"writable": []
},
"logMessages": [
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [1]",
"Program log: Instruction: SetAuthority",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1167 of 200000 compute units",
"Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success"
],
"postBalances": ["996479120", "2039280", "1141440"],
"postTokenBalances": [
{
"accountIndex": 1,
"mint": "ELRBdV4gcuxqYb6jHkV4ySJ7dwYx9344cgFNzUSww1ra",
"owner": "A9FK8XxT2Hfefz8H3vQJHLwvbibGQJWErBsqMumgUYeP",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "100",
"decimals": 2,
"uiAmount": "1",
"uiAmountString": "1"
}
}
],
"preBalances": ["996484120", "2039280", "1141440"],
"preTokenBalances": [
{
"accountIndex": 1,
"mint": "ELRBdV4gcuxqYb6jHkV4ySJ7dwYx9344cgFNzUSww1ra",
"owner": "DLvpDgEABKfEaRDz5Qh9tSrJhuZzsiiZMcYXmvdek1zV",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "100",
"decimals": 2,
"uiAmount": "1",
"uiAmountString": "1"
}
}
],
"rewards": [],
"status": {
"Ok": null
}
},
"slot": "137902",
"transaction": {
"message": {
"accountKeys": [
"DLvpDgEABKfEaRDz5Qh9tSrJhuZzsiiZMcYXmvdek1zV",
"5Qj4uNGuAEBdryPg8k2UTewpnNfYAc9Ux9fCcDrNAjGs",
"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
],
"addressTableLookups": [],
"header": {
"numReadonlySignedAccounts": 0,
"numReadonlyUnsignedAccounts": 1,
"numRequiredSignatures": 1
},
"instructions": [
{
"accounts": [1, 0],
"data": "bmb6sys4wqZErNeiV7hrM4vQVHF8AVBhi3XeR5TgbQM68MH",
"programIdIndex": 2,
"stackHeight": null
}
],
"recentBlockhash": "CvTdX9MSYkqFMkALUHeGPMQN5yBeUdJptRdce2qMEkPr"
},
"signatures": [
"3rHUaKMh4KDDfdaATAL4J5WDEV7oFm7ykkNaMf1Eo5EwvDUjLE6dWsbxNDmyENrhb2w5gE4KqRxZ3ZwQxuM18SVR"
]
},
"version": "0"
}

Token Yatırma Hesaplaması

Token yatırmalarını doğru şekilde takip etmek için işlem meta verilerindeki preTokenBalances ve postTokenBalance alanlarını karşılaştırmalısınız. Bu alanlar, işlem öncesi ve sonrasındaki token bakiyelerini ve token account sahibini göstererek transfer edilen token miktarını tam olarak hesaplamanıza olanak tanır. Bu yaklaşım, gerçek bakiye değişikliklerini yakalamanızı sağlar.

  • preTokenBalances ve postTokenBalances alanlarındaki owner alanı aynı kalırsa, amount alanları arasındaki farkı hesaplayın.
  • Token account sahipliği değişirse (preTokenBalances ile postTokenBalances arasında farklı owner alanı) ve postTokenBalance içindeki yeni sahip borsanızın beklenen owner adresiyle eşleşiyorsa, postTokenBalances içindeki amount alanında gösterilen bakiyenin tamamını yatırılan tutar olarak kabul edin.
Transaction Metadata
"meta": {
// --snip--
"postBalances": ["994375240", "2074080", "2074080", "1141440"],
"postTokenBalances": [
{
"accountIndex": 1,
"mint": "Fx1JZFeYbCxLrMv7422YSxpr7YzcsAgpU1MkjZTyCKi2",
"owner": "4fvXFPXSL9i7VbiRzoizuW4bhn1dMvRgQzQ6VevssYxw",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "0",
"decimals": 2,
"uiAmount": null,
"uiAmountString": "0"
}
},
{
"accountIndex": 2,
"mint": "Fx1JZFeYbCxLrMv7422YSxpr7YzcsAgpU1MkjZTyCKi2",
"owner": "8fjS2shNWY8xniiEMLNk1Aek4MAu8Qp2LCXJckVwTD4n",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "100",
"decimals": 2,
"uiAmount": "1",
"uiAmountString": "1"
}
}
],
"preBalances": ["994380240", "2074080", "2074080", "1141440"],
"preTokenBalances": [
{
"accountIndex": 1,
"mint": "Fx1JZFeYbCxLrMv7422YSxpr7YzcsAgpU1MkjZTyCKi2",
"owner": "4fvXFPXSL9i7VbiRzoizuW4bhn1dMvRgQzQ6VevssYxw",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "100",
"decimals": 2,
"uiAmount": "1",
"uiAmountString": "1"
}
},
{
"accountIndex": 2,
"mint": "Fx1JZFeYbCxLrMv7422YSxpr7YzcsAgpU1MkjZTyCKi2",
"owner": "8fjS2shNWY8xniiEMLNk1Aek4MAu8Qp2LCXJckVwTD4n",
"programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"uiTokenAmount": {
"amount": "0",
"decimals": 2,
"uiAmount": null,
"uiAmountString": "0"
}
}
],
// --snip--
}

Para Çekme

Kullanıcının belirttiği para çekme adresi, SOL cüzdan adresi olmalıdır.

Bir para çekme transferi gerçekleştirilmeden önce, borsa adresi yukarıda açıklandığı şekilde kontrol etmelidir. Bunun yanı sıra bu adres, System Program tarafından sahip olunmalı ve herhangi bir hesap verisi içermemelidir. Adresin SOL bakiyesi yoksa, para çekme işlemine devam etmeden önce kullanıcı onayı alınmalıdır. Diğer tüm para çekme adresleri reddedilmelidir.

Para çekme adresinden, doğru mint için Associated Token Account (ATA) türetilir ve transfer, bir TransferChecked talimatı aracılığıyla söz konusu hesaba gönderilir. ATA adresinin henüz mevcut olmayabileceğini unutmayın; bu durumda borsa, hesabı kullanıcı adına fonlamalıdır. SPL Token hesapları için para çekme hesabının fonlanması 0.00203928 SOL (2.039.280 lamport) gerektirecektir.

Para çekme için spl-token transfer komut şablonu:

spl-token transfer --fund-recipient <exchange token account> <withdrawal amount> <withdrawal address>

Diğer Hususlar

Dondurma Yetkisi

Yasal uyumluluk nedeniyle, bir SPL Token ihraç eden kuruluş, mint'iyle ilişkili olarak oluşturulan tüm hesaplar üzerinde isteğe bağlı olarak "Dondurma Yetkisi" tutmayı seçebilir. Bu, söz konusu hesaptaki varlıkları istedikleri zaman dondurmasına ve çözülene kadar hesabı kullanılamaz hale getirmesine olanak tanır. Bu özellik kullanılıyorsa, dondurma yetkisinin pubkey'i SPL Token'ın mint hesabında kayıtlı olacaktır.

SPL Token-2022 (Token Extensions) Standardı için Temel Destek

SPL Token-2022, Solana blok zincirinde sarılmış/sentetik token oluşturma ve değişimi için en yeni standarttır.

"Token Extensions" olarak da bilinen bu standart, token oluşturucuların ve hesap sahiplerinin isteğe bağlı olarak etkinleştirebileceği birçok yeni özellik içerir. Bu özellikler arasında gizli transferler, transfer ücretleri, mint kapatma, meta veri, kalıcı delegeler, değiştirilemez sahiplik ve çok daha fazlası yer almaktadır. Daha fazla bilgi için lütfen uzantı kılavuzuna bakın.

Borsanız SPL Token'ı destekliyorsa, SPL Token-2022'yi desteklemek için fazladan çok fazla çalışma gerekmez:

  • CLI aracı, sürüm 3.0.0'dan itibaren her iki programla da sorunsuz çalışır.
  • preTokenBalances ve postTokenBalances, SPL Token-2022 bakiyelerini içerir
  • RPC, SPL Token-2022 hesaplarını indeksler; ancak bunların TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb program id'si ile ayrı olarak sorgulanması gerekir

Associated Token Account aynı şekilde çalışır ve yeni hesap için gerekli SOL depozito miktarını doğru şekilde hesaplar.

Ancak uzantılar nedeniyle hesaplar 165 bayttan daha büyük olabilir; dolayısıyla fonlanmaları için 0.00203928 SOL'dan daha fazlası gerekebilir.

Örneğin, Associated Token Account programı her zaman "immutable owner" uzantısını içerir; bu nedenle hesaplar en az 170 bayt kaplar ve bu da 0.00207408 SOL gerektirir.

Uzantıya Özgü Hususlar

Önceki bölüm, SPL Token-2022 için en temel desteği özetlemektedir. Uzantılar token'ların davranışını değiştirdiğinden, borsaların token'ları nasıl işlediğini değiştirmesi gerekebilir.

Bir mint veya token account üzerindeki tüm uzantıları görmek mümkündür:

spl-token display <account address>

Transfer Ücreti

Bir token, transfer ücreti ile yapılandırılabilir; bu durumda transfer edilen token'ların bir kısmı gelecekteki tahsilat için hedefte alıkonulur.

Borsanız bu token'ları transfer ediyorsa, alıkonulan miktar nedeniyle tamamının hedefe ulaşmayabileceğine dikkat edin.

Sürprizlerden kaçınmak için transfer sırasında beklenen ücreti belirtmek mümkündür:

spl-token transfer --expected-fee <fee amount> --fund-recipient <exchange token account> <withdrawal amount> <withdrawal address>

Mint Kapatma Yetkisi

Bu uzantıyla birlikte, token oluşturucu token arzı sıfır olması koşuluyla bir mint'i kapatabilir.

Bir mint kapatıldığında, boş token account'lar hâlâ mevcut olabilir ve bunlar artık geçerli bir mint ile ilişkilendirilmeyecektir.

Bu token account'ları kapatmak güvenlidir:

spl-token close --address <account address>

Gizli Transfer

Mint'ler gizli transferler için yapılandırılabilir; böylece token miktarları şifrelenir ancak hesap sahipleri hâlâ kamuya açık kalır.

Borsalar, kullanıcı miktarlarını gizlemek amacıyla token account'larını gizli transfer gönderip almak için yapılandırabilir. Token account'larında gizli transferlerin etkinleştirilmesi zorunlu değildir; dolayısıyla borsalar kullanıcıları token'ları gizli olmayan şekilde göndermeye zorlayabilir.

Gizli transferleri etkinleştirmek için hesabın buna göre yapılandırılması gerekir:

spl-token configure-confidential-transfer-account --address <account address>

Transfer gerçekleştirmek için:

spl-token transfer --confidential <exchange token account> <withdrawal amount> <withdrawal address>

Gizli bir transfer sırasında preTokenBalance ve postTokenBalance alanları herhangi bir değişiklik göstermez. Depozito hesaplarını taramak için token'ları çekmek amacıyla yeni bakiyenin şifresini çözmeniz gerekir:

spl-token apply-pending-balance --address <account address>
spl-token withdraw-confidential-tokens --address <account address> <amount or ALL>

Varsayılan Hesap Durumu

Mint'ler varsayılan bir hesap durumuyla yapılandırılabilir; böylece tüm yeni token account'lar varsayılan olarak dondurulur. Bu token oluşturucular, kullanıcıların hesabı çözündürmek için ayrı bir süreçten geçmesini gerektirebilir.

Transfer Edilemez

Bazı token'lar transfer edilemez; ancak yine de yakılabilir ve hesap kapatılabilir.

Kalıcı Delege

Token oluşturucular, tüm token'ları için kalıcı bir delege atayabilir. Kalıcı delege, herhangi bir hesaptan token transfer edebilir veya yakabilir; bu da potansiyel olarak fonların çalınmasına yol açabilir.

Bu, belirli yargı bölgelerinde stabilcoinler için yasal bir gereklilik olabilir ya da token geri alım planları için kullanılabilir.

Bu token'ların borsanızın bilgisi olmadan transfer edilebileceğine dikkat edin.

Transfer Hook

Token'lar, transferi doğrulamak veya başka bir mantık yürütmek amacıyla transferler sırasında çağrılması gereken ek bir programla yapılandırılabilir.

Solana çalışma zamanı tüm hesapların bir programa açıkça aktarılmasını gerektirdiğinden ve transfer hook'ları ek hesaplar gerektirdiğinden, borsa bu token'lar için transfer talimatlarını farklı şekilde oluşturmalıdır.

CLI ve createTransferCheckedWithTransferHookInstruction gibi talimat oluşturucular, ekstra hesapları otomatik olarak ekler; ancak ek hesaplar açıkça da belirtilebilir:

spl-token transfer --transfer-hook-account <pubkey:role> --transfer-hook-account <pubkey:role> ...

Transfer Sırasında Zorunlu Açıklama Notu

Kullanıcılar, token account'larını transfer sırasında açıklama notu (memo) gerektirecek şekilde yapılandırabilir.

Borsaların token'ları kullanıcılara geri transfer etmeden önce bir memo talimatı eklemesi gerekebilir ya da kullanıcıların borsaya gönderim yapmadan önce bir memo talimatı eklemesini zorunlu kılabilirler:

spl-token transfer --with-memo <memo text> <exchange token account> <withdrawal amount> <withdrawal address>

Entegrasyonun Test Edilmesi

Mainnet'te üretime geçmeden önce tam iş akışınızı Solana devnet ve testnet kümelerinde test ettiğinizden emin olun. Devnet en açık ve esnek olanıdır; başlangıç geliştirmesi için idealdir. Testnet ise daha gerçekçi küme yapılandırması sunar. Hem devnet hem de testnet bir faucet destekler; geliştirme ve test için biraz devnet veya testnet SOL edinmek üzere solana airdrop 1 komutunu çalıştırın.

Is this page helpful?

İçindekiler

Node KurulumuOtomatik Yeniden Başlatma ve İzlemeYeni Yazılım Sürüm DuyurularıDefter SürekliliğiValidator Port Maruziyetini En Aza İndirmeYatırım Hesaplarının KurulumuSonuçÇevrimdışı HesaplarYatırımları DinlemeVersiyonlanmış İşlem MigrasyonuBloklar İçin YoklamaSonuçBlok Getirme İpuçlarıSonuçAdres GeçmişiSonuçSonuçPara Çekme İşlemleri GöndermeSenkronAsenkronİşlem Onayları ve KesinleşmeSonuçBlockhash Sona ErmesiPara Çekme İşlemleri İçin Kullanıcı Tarafından Sağlanan Hesap Adreslerinin DoğrulanmasıTemel DoğrulamaGelişmiş DoğrulamaGeçerli ed25519 pubkey KontrolüJavaMinimum Para Yatırma ve Çekme TutarlarıSonuçÖnceliklendirme Ücretleri ve Hesaplama BirimleriÖnceliklendirme Ücreti Nedir?Önceliklendirme Ücreti Ne Kadar Olmalıdır?Önceliklendirme Ücretleri Nasıl Uygulanır?Önceliklendirme Ücretleri ve Kalıcı Nonce'larSPL Token Standardının DesteklenmesiToken Mint'lerispl-token CLI Aracının YüklenmesiAccount OluşturmaKomut SatırıÖrnekAccount Bakiyesini Kontrol EtmeKomut SatırıÖrnekToken TransferleriKomut SatırıÖrnekYatırmaÖrnek 1: Tek Token TransferiÖrnek 2: Token Account Oluşturma ve TransferÖrnek 3: Token Account Sahibini DeğiştirmeToken Yatırma HesaplamasıPara ÇekmeDiğer HususlarDondurma YetkisiSPL Token-2022 (Token Extensions) Standardı için Temel DestekUzantıya Özgü HususlarTransfer ÜcretiMint Kapatma YetkisiGizli TransferVarsayılan Hesap DurumuTransfer EdilemezKalıcı DelegeTransfer HookTransfer Sırasında Zorunlu Açıklama NotuEntegrasyonun Test Edilmesi
Sayfayı Düzenle
© 2026 Solana Vakfı. Tüm hakları saklıdır.