Ana içeriğe geç

Bir DeFi uygulaması oluşturun ve dağıtın

soliditydefiweb3.jstruffleganacheakıllı sözleşmeler
Orta düzey
strykerin
github.com(opens in a new tab)
31 Aralık 2020
9 dakikalık okuma minute read

Bu öğreticide, kullanıcıların akıllı sözleşmeye bir ERC20 token'ı yatırabilecekleri ve Farm Token'larını basıp onlara aktarabilecekleri, Solidity ile bir DeFi Uygulaması oluşturacağız. Kullanıcılar daha sonra Farm Token'larını akıllı sözleşmede yakarak ERC20 token'larını geri çekebilirler ve ERC20 token'ları kendilerine geri aktarılır.

Truffle ve Ganache kurun

İlk kez bir akıllı sözleşme yazıyorsanız, ortamınızı ayarlamanız gerekecektir. İki araç kullanacağız: Truffle(opens in a new tab) ve Ganache(opens in a new tab).

Truffle, Ethereum için akıllı sözleşmeler geliştirmek için bir geliştirme ortamı ve test çerçevesidir. Truffle ile akıllı sözleşmeler oluşturmak ve blok zincirine yerleştirmek kolaydır. Ganache, akıllı sözleşmeleri test etmek için yerel bir Ethereum blok zinciri oluşturmamıza izin veriyor. Gerçek ağın özelliklerini simüle eder ve ilk 10 hesap 100 test ether'ı ile finanse edilir, böylece akıllı sözleşme dağıtımını ve testini ücretsiz ve kolay hâle getirir. Ganache, bir masaüstü uygulaması ve bir komut satırı aracı olarak mevcuttur. Bu makale için UI masaüstü uygulamasını kullanacağız.

Ganache UI masaüstü uygulaması(opens in a new tab)Ganache UI masaüstü uygulaması

Projeyi oluşturmak için aşağıdaki komutları çalıştırın

mkdir your-project-name
cd your-project-name
truffle init

Bu, akıllı sözleşmelerimizin geliştirilmesi ve uygulanması için boş bir proje oluşturacaktır. Oluşturulan proje yapısı aşağıdaki gibidir:

  • contracts: Solidity akıllı sözleşmeleri için klasör

  • migrations: Dağıtım komut dosyaları için klasör

  • test: Akıllı sözleşmelerimizin testi için klasör

  • truffle-config.js: Truffle yapılandırma dosyası

ERC20 Token'ını Oluşturun

Öncelikle akıllı sözleşmede stake etmek için kullanacağımız ERC20 token'ımızı oluşturmamız gerekiyor. Değiştirilebilir simgemizi oluşturmak için önce OpenZeppelin kütüphanesini kurmamız gerekecek. Bu kütüphane, ERC20 ve ERC721 gibi standartların uygulamalarını içerir. Onu kurmak için, şu komutu çalıştırın:

npm install @openzeppelin/contracts

OpenZeppelin kütüphanesini kullanarak, aşağıdaki solidity koduyla contracts/MyToken.sol'a yazarak ERC20 token'ımızı oluşturabiliriz:

1pragma solidity ^0.8.0;
2
3import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4
5contract MyToken is ERC20 {
6 constructor() public ERC20("MyToken", "MTKN"){
7 _mint(msg.sender, 1000000000000000000000000);
8 }
9}
Tümünü göster
Kopyala

Yukarıdaki kodda:

    1. Satır: openzeppelin'den bu token standardının uygulamasını içeren ERC20.sol sözleşmesini içe aktarıyoruz.
    1. Satır: ERC20.sol sözleşmesinden kalıtıma uğruyoruz.
  • Satır 6: ERC20.sol yapıcısını çağırıp ad ve sembol parametrelerini sırasıyla "MyToken" ve "MTKN" olarak geçiyoruz.

    1. Satır: Akıllı sözleşmeyi dağıtan hesap için 1 milyon token basıyor ve aktarıyoruz (ERC20 token'ı için varsayılan 18 ondalık basamağı kullanıyoruz. Bu, 1 token basmak istiyorsak, onu 10000000000000000000, 1'den sonra 18 sıfır, olarak temsil edeceğiniz anlamına gelir).

_decimals alanının 18 olarak ayarlandığı ERC20.sol yapıcı uygulamasını aşağıda görebiliriz:

1string private _name;
2string private _symbol;
3uint8 private _decimals;
4
5constructor (string memory name_, string memory symbol_) public {
6 _name = name_;
7 _symbol = symbol_;
8 _decimals = 18;
9}
Tümünü göster
Kopyala

ERC20 Token'ını Derleyin

Akıllı sözleşmemizi derlemek için önce solidity derleyici versiyonumuzu kontrol etmeliyiz. Bunu şu komutu çalıştırarak kontrol edebilirsiniz:

truffle version

Varsayılan versiyon Solidity v0.5.16'dır. Token'ımız 0.6.2 solidity versiyonu kullanılarak yazıldığından, sözleşmelerimizi derlemek için komutu çalıştırırsak bir derleyici hatası alırız. Hangi solidity derleyici sürümünün kullanılacağını belirlemek için truffle-config.js dosyasına gidin ve aşağıda görüldüğü gibi istediğiniz derleyici sürümüne ayarlayın:

1// Configure your compilers
2compilers: {
3 solc: {
4 version: "^0.8.0", // Fetch exact version from solc-bin (default: truffle's version)
5 // docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
6 // settings: { // See the solidity docs for advice about optimization and evmVersion
7 // optimizer: {
8 // enabled: false,
9 // runs: 200
10 // },
11 // evmVersion: "byzantium"
12 // }
13 }
14}
Tümünü göster

Şimdi aşağıdaki komutu çalıştırarak akıllı sözleşmemizi derleyebiliriz:

truffle compile

ERC20 Token'ını Dağıtın

Derlemeden sonra artık token'ımızı dağıtabiliriz.

migrations klasöründe, 2_deploy_Tokens.js isimli bir dosya oluşturun. Bu dosya, hem ERC20 Token'ımızı hem de FarmToken akıllı sözleşmemizi dağıtacağımız yerdir. Aşağıdaki kod, MyToken.sol sözleşmemizi dağıtmak için kullanılır:

1const MyToken = artifacts.require("MyToken")
2
3module.exports = async function (deployer, network, accounts) {
4 // Deploy MyToken
5 await deployer.deploy(MyToken)
6 const myToken = await MyToken.deployed()
7}

Ganache'yi açın ve yerel bir Ethereum blok zinciri başlatmak için "Quickstart" (Hızlı Başlangıç) seçeneğini seçin. Sözleşmemizi dağıtmak için, şunu çalıştırın:

truffle migrate

Sözleşmelerimizi dağıtmak için kullanılan adres, Ganache'nin bize gösterdiği adresler listesinden ilkidir. Bunu doğrulamak için Ganache masaüstü uygulamasını açabiliriz ve akıllı sözleşmelerimizi dağıtmak için ether maliyeti nedeniyle ilk hesap için ether bakiyesinin düştüğünü doğrulayabiliriz:

Ganache masaüstü uygulaması(opens in a new tab)Ganache masaüstü uygulaması

Dağıtıcı adresine 1 milyon MyToken token'ının gönderildiğini doğrulamak amacıyla, dağıtılan akıllı sözleşmemizle etkileşim kurmak için Truffle Konsolunu kullanabiliriz.

Truffle Konsolu, tüm Ethereum istemcilerine bağlanan temel bir etkileşimli konsoldur.(opens in a new tab)

Akıllı sözleşmemizle etkileşim kurmak için aşağıdaki komutu çalıştırın:

truffle console

Artık sıradaki komutları terminale yazabiliriz:

  • Akıllı sözleşmeyi alın: myToken = await MyToken.deployed()

  • Ganache'dan hesap dizisini alın: accounts = await web3.eth.getAccounts()

  • İlk hesabın bakiyesini alın: balance = await myToken.balanceOf(accounts[0])

  • Bakiyeyi 18 ondalık basamak olarak biçimlendirin: web3.utils.fromWei(balance.toString())

Yukarıdaki komutları çalıştırarak, ilk adreste gerçekten de 1 milyon MyTokens olduğunu göreceğiz:

İlk adreste 1000000 MyToken var(opens in a new tab)

İlk adreste 1000000 MyToken var

FarmToken Akıllı Sözleşmesini Oluşturun

FarmToken akıllı sözleşmesinin 3 fonksiyonu olacaktır:

  • balance(): FarmToken akıllı sözleşmesindeki MyToken bakiyesini alın.

  • deposit(uint256 _amount): MyToken'ı kullanıcı adına FarmToken akıllı sözleşmesine aktarın ve ardından FarmToken'ı kullanıcıya aktarın.

  • withdraw(uint256 _amount): Kullanıcının FarmToken token'larını yakın ve MyToken token'larını kullanıcının adresine aktarın.

FarmToken yapıcısına bakalım:

1pragma solidity ^0.6.2;
2
3import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
4import "@openzeppelin/contracts/utils/Address.sol";
5import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
6import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
7
8contract FarmToken is ERC20 {
9 using Address for address;
10 using SafeMath for uint256; // As of Solidity v0.8.0, mathematical operations can be done safely without the need for SafeMath
11 using SafeERC20 for IERC20;
12
13 IERC20 public token;
14
15 constructor(address _token)
16 public
17 ERC20("FarmToken", "FRM")
18 {
19 token = IERC20(_token);
20 }
Tümünü göster
Kopyala
  • 3.-6. Satır: Openzeppelin'den şu sözleşmeleri içe aktarıyoruz: IERC20.sol, Address.sol, SafeERC20.sol ve ERC20.sol.

  • Satır 8: FarmToken, ERC20 sözleşmesinden kalıtıma uğrar.

  • 14.-19. Satır: FarmToken yapıcısı parametre olarak MyToken sözleşmesinin adresini alacak ve onun sözleşmesini token adlı genel değişkenimize atayacağız.

Hadi balance() fonksiyonunu uygulayalım. Hiçbir parametre almayacak ve bu akıllı sözleşmedeki MyToken bakiyesini iade edecek. Aşağıda gösterildiği gibi uygulanır:

1function balance() public view returns (uint256) {
2 return token.balanceOf(address(this));
3}
Kopyala

deposit(uint256 _amount) fonksiyonu için, kullanıcının yatırmak istediği miktarı parametre olarak alacak ve FarmToken token'larını basıp kullanıcıya aktaracak:

1function deposit(uint256 _amount) public {
2 // Amount must be greater than zero
3 require(_amount > 0, "amount cannot be 0");
4
5 // Transfer MyToken to smart contract
6 token.safeTransferFrom(msg.sender, address(this), _amount);
7
8 // Mint FarmToken to msg sender
9 _mint(msg.sender, _amount);
10}
Tümünü göster
Kopyala

withdraw(uint256 _amount) fonksiyonu için, kullanıcının yakmak istediği FarmToken miktarını parametre olarak alacağız ve ardından aynı miktarda MyToken'ı kullanıcıya geri aktaracağız:

1function withdraw(uint256 _amount) public {
2 // Burn FarmTokens from msg sender
3 _burn(msg.sender, _amount);
4
5 // Transfer MyTokens from this smart contract to msg sender
6 token.safeTransfer(msg.sender, _amount);
7}
Kopyala

Şimdi akıllı sözleşmemizi dağıtacağız. Bunu yapmak için 2_deploy_Tokens.js dosyasına geri döneceğiz ve dağıtılacak yeni sözleşmeyi ekleyeceğiz:

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (deployer, network, accounts) {
5 // Deploy MyToken
6 await deployer.deploy(MyToken)
7 const myToken = await MyToken.deployed()
8
9 // Deploy Farm Token
10 await deployer.deploy(FarmToken, myToken.address)
11 const farmToken = await FarmToken.deployed()
12}
Tümünü göster

FarmToken'ı dağıtırken, dağıtılmış MyToken sözleşmesinin adresini parametre olarak ilettiğimizi unutmayın.

Şimdi, sözleşmelerimizi dağıtmak için, truffle compile ve truffle migrate komutlarını çalıştırın.

Akıllı sözleşmemizi test edelim. Akıllı sözleşmemizle etkileşim kurmak için truffle console kullanmak yerine, bu işlemi otomatikleştirmek için bir komut dosyası oluşturacağız. scripts isimli bir klasör oluşturun ve aşağıdaki getMyTokenBalance.js dosyasını ekleyin. FarmToken akıllı sözleşmesindeki MyToken bakiyesini kontrol edecek:

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (callback) {
5 myToken = await MyToken.deployed()
6 farmToken = await FarmToken.deployed()
7 balance = await myToken.balanceOf(farmToken.address)
8 console.log(web3.utils.fromWei(balance.toString()))
9 callback()
10}
Tümünü göster

Bu komut dosyasını çalıştırmak için aşağıdaki cli komutunu çalıştırın:

truffle exec .\scripts\getMyTokenBalance.js

0 olan beklenen sonucu alacağız. FarmToken'ın henüz dağıtılmadığına dair bir hata alırsanız truffle ağı, sözleşme kodunuzun en son sürümünü almamıştır. Ganache'yi kapatın, yeniden hızlı bir şekilde başlatın ve truffle migrate'i çalıştırdığınızdan emin olun.

Şimdi, akıllı sözleşmeye MyToken stake edelim. deposit(uint256 _amount) fonksiyonu ERC20'den safeTransferFrom fonksiyonunu çağırdığından, kullanıcının MyToken'ı kullanıcı adına aktarmak için önce akıllı sözleşmeyi onaylaması gerekir. Yani aşağıdaki komut dosyasında önce bu adımı onaylayacağız, sonra fonksiyonu çağıracağız:

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (callback) {
5 const accounts = await new web3.eth.getAccounts()
6 const myToken = await MyToken.deployed()
7 const farmToken = await FarmToken.deployed()
8
9 // Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner through transferFrom.
10 // This is zero by default.
11 const allowanceBefore = await myToken.allowance(
12 accounts[0],
13 farmToken.address
14 )
15 console.log(
16 "Amount of MyToken FarmToken is allowed to transfer on our behalf Before: " +
17 allowanceBefore.toString()
18 )
19
20 // In order to allow the Smart Contract to transfer to MyToken (ERC-20) on the accounts[0] behalf,
21 // we must explicitly allow it.
22 // We allow farmToken to transfer x amount of MyToken on our behalf
23 await myToken.approve(farmToken.address, web3.utils.toWei("100", "ether"))
24
25 // Validate that the farmToken can now move x amount of MyToken on our behalf
26 const allowanceAfter = await myToken.allowance(accounts[0], farmToken.address)
27 console.log(
28 "Amount of MyToken FarmToken is allowed to transfer on our behalf After: " +
29 allowanceAfter.toString()
30 )
31
32 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer
33 balanceMyTokenBeforeAccounts0 = await myToken.balanceOf(accounts[0])
34 balanceMyTokenBeforeFarmToken = await myToken.balanceOf(farmToken.address)
35 console.log("*** My Token ***")
36 console.log(
37 "Balance MyToken Before accounts[0] " +
38 web3.utils.fromWei(balanceMyTokenBeforeAccounts0.toString())
39 )
40 console.log(
41 "Balance MyToken Before TokenFarm " +
42 web3.utils.fromWei(balanceMyTokenBeforeFarmToken.toString())
43 )
44
45 console.log("*** Farm Token ***")
46 balanceFarmTokenBeforeAccounts0 = await farmToken.balanceOf(accounts[0])
47 balanceFarmTokenBeforeFarmToken = await farmToken.balanceOf(farmToken.address)
48 console.log(
49 "Balance FarmToken Before accounts[0] " +
50 web3.utils.fromWei(balanceFarmTokenBeforeAccounts0.toString())
51 )
52 console.log(
53 "Balance FarmToken Before TokenFarm " +
54 web3.utils.fromWei(balanceFarmTokenBeforeFarmToken.toString())
55 )
56 // Call Deposit function from FarmToken
57 console.log("Call Deposit Function")
58 await farmToken.deposit(web3.utils.toWei("100", "ether"))
59 console.log("*** My Token ***")
60 balanceMyTokenAfterAccounts0 = await myToken.balanceOf(accounts[0])
61 balanceMyTokenAfterFarmToken = await myToken.balanceOf(farmToken.address)
62 console.log(
63 "Balance MyToken After accounts[0] " +
64 web3.utils.fromWei(balanceMyTokenAfterAccounts0.toString())
65 )
66 console.log(
67 "Balance MyToken After TokenFarm " +
68 web3.utils.fromWei(balanceMyTokenAfterFarmToken.toString())
69 )
70
71 console.log("*** Farm Token ***")
72 balanceFarmTokenAfterAccounts0 = await farmToken.balanceOf(accounts[0])
73 balanceFarmTokenAfterFarmToken = await farmToken.balanceOf(farmToken.address)
74 console.log(
75 "Balance FarmToken After accounts[0] " +
76 web3.utils.fromWei(balanceFarmTokenAfterAccounts0.toString())
77 )
78 console.log(
79 "Balance FarmToken After TokenFarm " +
80 web3.utils.fromWei(balanceFarmTokenAfterFarmToken.toString())
81 )
82
83 // End function
84 callback()
85}
Tümünü göster

Bu komut dosyasını çalıştırmak için: truffle exec .\scripts\transferMyTokenToFarmToken.js. Konsolunuzda şunu görmelisiniz:

transferMyTokenToFarmToken.js çıktısı(opens in a new tab)

transferMyTokenToFarmToken.js çıktısı

Gördüğümüz gibi, ilk hesapta FarmToken'ları olduğu için MyToken'ları akıllı sözleşmeye başarıyla yatırdık.

Çekmek için:

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (callback) {
5 const accounts = await new web3.eth.getAccounts()
6 const myToken = await MyToken.deployed()
7 const farmToken = await FarmToken.deployed()
8
9 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer
10 balanceMyTokenBeforeAccounts0 = await myToken.balanceOf(accounts[0])
11 balanceMyTokenBeforeFarmToken = await myToken.balanceOf(farmToken.address)
12 console.log("*** My Token ***")
13 console.log(
14 "Balance MyToken Before accounts[0] " +
15 web3.utils.fromWei(balanceMyTokenBeforeAccounts0.toString())
16 )
17 console.log(
18 "Balance MyToken Before TokenFarm " +
19 web3.utils.fromWei(balanceMyTokenBeforeFarmToken.toString())
20 )
21
22 console.log("*** Farm Token ***")
23 balanceFarmTokenBeforeAccounts0 = await farmToken.balanceOf(accounts[0])
24 balanceFarmTokenBeforeFarmToken = await farmToken.balanceOf(farmToken.address)
25 console.log(
26 "Balance FarmToken Before accounts[0] " +
27 web3.utils.fromWei(balanceFarmTokenBeforeAccounts0.toString())
28 )
29 console.log(
30 "Balance FarmToken Before TokenFarm " +
31 web3.utils.fromWei(balanceFarmTokenBeforeFarmToken.toString())
32 )
33
34 // Call Deposit function from FarmToken
35 console.log("Call Withdraw Function")
36 await farmToken.withdraw(web3.utils.toWei("100", "ether"))
37
38 console.log("*** My Token ***")
39 balanceMyTokenAfterAccounts0 = await myToken.balanceOf(accounts[0])
40 balanceMyTokenAfterFarmToken = await myToken.balanceOf(farmToken.address)
41 console.log(
42 "Balance MyToken After accounts[0] " +
43 web3.utils.fromWei(balanceMyTokenAfterAccounts0.toString())
44 )
45 console.log(
46 "Balance MyToken After TokenFarm " +
47 web3.utils.fromWei(balanceMyTokenAfterFarmToken.toString())
48 )
49
50 console.log("*** Farm Token ***")
51 balanceFarmTokenAfterAccounts0 = await farmToken.balanceOf(accounts[0])
52 balanceFarmTokenAfterFarmToken = await farmToken.balanceOf(farmToken.address)
53 console.log(
54 "Balance FarmToken After accounts[0] " +
55 web3.utils.fromWei(balanceFarmTokenAfterAccounts0.toString())
56 )
57 console.log(
58 "Balance FarmToken After TokenFarm " +
59 web3.utils.fromWei(balanceFarmTokenAfterFarmToken.toString())
60 )
61
62 // End function
63 callback()
64}
Tümünü göster

Bu komut dosyasını çalıştırmak için: truffle exec .\scripts\withdrawMyTokenFromTokenFarm.js. Aşağıdaki çıktıda görebileceğimiz gibi, MyTokens'i başarıyla geri aldık ve FarmTokens'i yaktık:

withdrawMyTokenFromTokenFarm.js çıktısı(opens in a new tab)

withdrawMyTokenFromTokenFarm.js çıktısı

Referanslar

Sözleşmeler - OpenZeppelin Belgeleri(opens in a new tab)

Akıllı Sözleşmeler için Güzel Araçlar | Truffle Suite(opens in a new tab)

Ganache | Truffle Suite(opens in a new tab)

DeFi nedir? Başlangıç Rehberi (2021 Güncellendi) (99bitcoins.com)(opens in a new tab)

DeFi - DeFi Llama'da Merkeziyetsiz Finans Lider Tablosu(opens in a new tab)

Son düzenleme: @kaanmetu(opens in a new tab), 23 Kasım 2023

Bu rehber yararlı oldu mu?