Blockchain teknolojisi, finansal işlemlerin güvenli ve şeffaf bir şekilde gerçekleştirilmesine olanak tanıyan önemli bir dönüşüm sağlıyor. Solana, yüksek performanslı ve ölçeklenebilir bir blockchain platformu olarak, inovasyonu destekleyen bir ekosistem sunuyor. Bu yazıda, Solana'nın güçlü altyapısından yararlanarak, TypeScript ve Nest.js kullanarak bir Wallet Backend'i oluşturmanın adımlarını keşfedeceğiz.
Solana Cookbook, Solana platformuna yönelik kapsamlı bir kaynak ve rehber koleksiyonudur. Bu yazıda, Cookbook'un sağladığı bilgilerden faydalanarak, Wallet Backend'inin nasıl oluşturulacağını adım adım göstereceğiz. Wallet Backend'i, Solana blockchaini ile etkileşime geçerek cüzdan oluşturma, bakiye sorgulama, işlem gönderme gibi temel işlevleri gerçekleştirecek bir arka uç hizmetidir.
Bu proje için TypeScript'i seçmemizin nedeni, statik tiplerin gücünü kullanarak daha güvenli ve hatasız bir geliştirme süreci sağlamaktır. Ayrıca, Nest.js kullanarak güçlü bir TypeScript tabanlı bir framework ile projemizi hızlı ve yapılandırılabilir bir şekilde geliştirebileceğiz. Nest.js, modüler bir yapı sunarak, kodun daha temiz ve bakımı kolay olmasını sağlar.
Hazırsanız, Solana Cookbook referans alınarak, TypeScript ve Nest.js ile Solana için bir Wallet Backend'i oluşturmanın heyecan verici yolculuğuna başlayalım!
Öncelikle kullanacağımız teknolojileri kurmamız gerekiyor. Bu yazıda, Node.js 14.17.0 LTS sürümünü kullanacağız. Node.js'i indirmek için, https://nodejs.org/en/download/ adresini ziyaret edebilirsiniz. Node.js'i kurduktan sonra, Nest.js'i kurmak için aşağıdaki komutu çalıştırabilirsiniz:
npm i -g @nestjs/cli
Nest.js, TypeScript tabanlı bir framework olduğu için, TypeScript'i de kurmamız gerekiyor. TypeScript'i kurmak için aşağıdaki komutu çalıştırabilirsiniz:
npm i -g typescript
Şimdi kullanacağımız paketleri kuralım. Öncelikle, Nest.js'in sağladığı paketleri kurmak için aşağıdaki komutu çalıştırabilirsiniz: Npm desteğini seçmeyi unutmayın çünkü yazının devamında npm paket yöneticisini kullanacağız.
nest new solana-wallet
Solonayla iletişme geçmek için @solona/web3.js paketini kullanacağız. Bu paketi kurmak için aşağıdaki komutu çalıştırabilirsiniz:
npm i @solana/web3.js
Yazacağımız apileri denemek için swagger kullanacağız. Swagger'ı kurmak için aşağıdaki komutu çalıştırabilirsiniz:
npm install --save @nestjs/swagger
Swagger'ı yükledikten sonra kullanmak için main.ts dosyasında birkaç değişiklik yapmamız gerekiyor. main.ts dosyasını aşağıdaki gibi değiştirebilirsiniz:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
Bu işlemlerden sonra projemizi çalıştırabiliriz. Önce genel paketleri yüklemesini sağlayıp sonra projemizi çalıştıralım:
npm install
npm run start:dev
Bu noktada artık dosya dizininiz aşağıdaki gibi görünmelidir: Projenizi çalıştırdıktan sonra, http://localhost:3000/api adresine giderek Swagger'ı kullanabilirsiniz. Swagger'ı kullanarak, projenizin API'lerini test edebilirsiniz. Aşağıdaki gibi bir ekran göreceksiniz: Artık temel bir proje kurulumumuz var. Şimdi, Solana ile etkileşime geçmek için gerekli olan hesap oluşturma, bakiye sorgulama, işlem gönderme gibi işlevleri gerçekleştirecek olan Wallet Service'i oluşturmaya başlayabiliriz. Terminaliniz proje dizinindeyse, sırasıyla aşağıdaki komutları çalıştırarak Wallet Service'i oluşturalım:
cd src
mkdir routes
cd routes
nest g module wallet
nest g service wallet
nest g controller wallet
Gözünüze batmaması ve aklınızı karıştırmaması için aşağıdaki dosyaları silebilirsiniz:
src/app.controller.ts
src/app.service.ts
src/app.controller.spec.ts
src/app.module.ts
src/routes/wallet/wallet.controller.spec.ts
src/routes/wallet/wallet.service.spec.ts
Ayrıca main.ts içindeki AppModule olan kısmı WalletModule olarak değiştirebilirsiniz.
async function bootstrap() {
const app = await NestFactory.create(WalletModule);
...
}
Bu işlemlerden sonra dosya dizininiz aşağıdaki gibi görünmelidir:
Bu kısımda artık Solana ile etkileşime geçmek için gerekli olan hesap oluşturma, bakiye sorgulama, işlem gönderme gibi işlevleri gerçekleştirecek olan Wallet Service'i oluşturmaya başlayacağız.
wallet.service.ts dosyasını gidelim ve keypair oluşturup bunu döndüren bir fonksiyon yazalım. Bu fonksiyonu yazarken, Solana'nın sağladığı @solana/web3.js paketini kullanacağız. Bu paketin sağladığı KeyPair sınıfını kullanarak keypair oluşturacağız. KeyPair sınıfı, Solana'da bir hesabı temsil eder. Bu hesabın public ve private keylerini içerir. Bu keypair'i oluşturmak için aşağıdaki fonksiyonu yazabilirsiniz:
import { Injectable } from '@nestjs/common';
import { Keypair } from '@solana/web3.js';
@Injectable()
export class WalletService {
async generateKeyPair() {
const main_account: Keypair = await Keypair.generate();
const public_key: string = main_account.publicKey.toBase58();
const secret_key = main_account.secretKey.toString();
return { publicKey: public_key, secretKey: secret_key };
}
}
Şimdi bunu gidip wallet.controller.ts dosyasında kullanalım. Bunun için öncelikle wallet.service.ts dosyasını import edelim. Sonra da WalletController sınıfının constructor'ına WalletService'i inject edelim. Son olarak da generateKeyPair fonksiyonunu çağıralım. Bunun için aşağıdaki gibi bir kod yazabilirsiniz:
import { Controller, Get } from '@nestjs/common';
import { WalletService } from './wallet.service';
@Controller('wallet')
export class WalletController {
constructor(private walletService : WalletService) {
}
@Get()
async generateKeyPair() {
return await this.walletService.generateKeyPair();
}
}
Bu işlemlerden sonra http://localhost:3000/api adresine giderek Swagger üzerinden yeni apimizi test edebiliriz. Aşağıdaki gibi bir ekran göreceksiniz: Şimdi gidip epoint'i test edelim. Test etmek için "Try it out" butonuna tıklayıp "Execute" butonuna tıklayabilirsiniz. Aşağıdaki gibi bir ekran göreceksiniz: İsterseniz gidip SolScan ile oluşturduğunuz walletı teyit edebilirsiniz. Bunun için aşağıdaki linki kullanabilirsiniz:
https://solscan.io/account/<public_key>
Şimdi de wallet.service.ts dosyasına gidip bakiye sorgulama fonksiyonunu yazalım. Bunun için öncelikle @solana/web3.js paketini import edelim. Sonra da aşağıdaki fonksiyonu yazabilirsiniz:
async showBalance(publicKey:string){
const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
let wallet = new PublicKey(publicKey);
let balance = await connection.getBalance(wallet);
console.log(`${balance / LAMPORTS_PER_SOL} SOL`);
return {balance:`${balance} LAMPORTS`};
}
Şimdi gidip wallet.controller.ts dosyasında kullanalım. Bunun için öncelikle wallet.service.ts dosyasını import edelim. Sonra da WalletController sınıfının constructor'ına WalletService'i inject edelim. Son olarak da showBalance fonksiyonunu çağıralım. Bunun için aşağıdaki gibi bir kod yazabilirsiniz:
@Get('balance/:publicKey')
async getBalance(@Query('publicKey') publicKey:string) {
return await this.walletService.showBalance(publicKey);
}
Bu işlemlerden sonra http://localhost:3000/api adresine gidip yeni apimizi test edelim. Önce daha önce yazdığım apiden publicKey yaratalım sonra yeni yazdığım showBalance apisinden bakiye sorgulaması yapalım.
Transaction fonksiyonunu yazmadan önce AirDrop isteme işlemini yapmamız gerekiyor. Bunun için aşağıdaki fonksiyonu yazabilirsiniz:
async requestAirdrop(publicKey:string,solBalance:number){
const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
let wallet : PublicKey = new PublicKey(publicKey);
let signature = await connection.requestAirdrop(wallet, LAMPORTS_PER_SOL);
await connection.confirmTransaction(signature);
return {signature:signature};
}
Şimdi gidip wallet.controller.ts dosyasında kullanalım. Bunun için öncelikle wallet.service.ts dosyasını import edelim. Sonra da WalletController sınıfının constructor'ına WalletService'i inject edelim. Son olarak da requestAirdrop fonksiyonunu çağıralım. Bunun için aşağıdaki gibi bir kod yazabilirsiniz:
@Get('airdrop/:publicKey/:solBalance')
async requestAirdrop(@Query('publicKey') publicKey:string,@Query('solBalance') solBalance:number) {
return await this.walletService.requestAirdrop(publicKey,solBalance);
}
Bu işlemlerden sonra http://localhost:3000/api adresine gidip yeni apimizi test edelim. Daha önce yazdığımız apiden publicKey yaratalım sonra yeni yazdığımız requestAirdrop apisinden airdrop isteğini gönderelim. Şimdi gidip SolScan ile teyite edebilir ya da daha önce yazdığımız showBalance apisini kullanabilirsiniz. SolScan için aşağıdaki linki kullanabilirsiniz:
https://solscan.io/account/<public_key>
Apiden ise aşağıdaki gibi bir sonuç alacaksınız:
Transaction fonksiyonunu yazmadan önce bir model yazacağız. Bunun için öncellikle şu komutu çalıştırın:
cd wallet
touch wallet.dto.ts
cd ..
Şimdi wallet.dto.ts dosyasına gidip aşağıdaki kodları yazalım:
import { ApiProperty } from "@nestjs/swagger";
export class TransactionDto{
@ApiProperty({ type: String, required: true, example:"[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"})
privateKey: string;
//dikkat bu benim hesabımın public keyi
@ApiProperty({ type: String, required: true, example:'99Grs8xvqrox8Zgcp2AgBVvWTBdBQp37YNgfzwLmsQ1P'})
reciver_public_key: string;
@ApiProperty({ type: Number, required: true, example:10000000})
balance:number;
}
Şimdi gidip wallet.service.ts dosyasına gidip trantaction fonksiyonunu yazalım:
async transaction(item: TransactionDto) {
{
const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
const keyPair: Keypair = Keypair.fromSecretKey(new Uint8Array(JSON.parse(item.privateKey)));
const transferTransaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: keyPair.publicKey,
toPubkey: new PublicKey(item.reciver_public_key),
lamports: item.balance,
})
);
const signature: string = await sendAndConfirmTransaction(connection, transferTransaction, [
keyPair
])
return signature;
}
}
Hadi şimdi gidip apimizi hazırlayayım, bu sefer diğerlerinden farklı olarak post methodunu kullanacağız. Wallet.controller.ts dosyasına gidip aşağıdaki kodları yazalım:
@Post('transaction')
async transaction(@Body() item:TransactionDto) {
return await this.walletService.transaction(item);
}
Bu işlemlerden sonra http://localhost:3000/api adresine gidip yeni apimizi test edelim. Daha önce airDrop istediğimiz hesabın SecretKeyini ve yeni yaratacağımız hesabın public keyini alalım. Sonra da aşağıdaki gibi bir body ile post işlemi yapalım: Bundan sonra dönen veriyi https://solscan.io/?cluster=devnet adresinde teyit edebilirsiniz. Aşağıdaki gibi görünecektir: İstersek daha önce yazdığımız showBalance apisi ile de bakiye sorgulaması yapabiliriz. Aşağıdaki gibi görünecektir:
Bu yazıda NestJS ile Solana Blockchain'ini kullanarak bir wallet uygulaması geliştirdik. Solona kodlarının büyük çoğunluğu Solona Cookbook'dan alınmıştır. Bu güzel kaynak için Solona Foundation'a teşekkür ederim. Umarım bu yazı sizin Solana Blockchain'ininde program geliştirmenize önayak olabilmiştir. Aklınızda kalan sorular için bana linknedin ve telegram adreslerimden ulaşabilirsiniz, bu projeye issue olarak da açabilirsiniz. Bir sonraki yazıda görüşmek üzere. Hoşçakalın.