Lanjut ke konten utama

Buat dan sebarkan aplikasi DeFi

soliditydefiweb3.jstruffleganachekontrak pintar
Tingkat menengah
strykerin
github.com(opens in a new tab)
31 Desember 2020
10 bacaan singkat minute read

Dalam tutorial ini, kita akan membangun Aplikasi DeFi dengan Solidity di mana pengguna dapat menyetor token ERC20 ke kontrak pintar dan kontrak pintar akan mencetak serta mentransfer Token Farm ke mereka. Pengguna nantinya dapat menarik token ERC20 mereka dengan membakar Token Farm pada kontrak pintar dan token ERC20 akan ditransfer kembali ke mereka.

Instal Truffle dan Ganache

Jika ini adalah pertama kalinya Anda menulis kontrak pintar, maka Anda perlu mengatur lingkungan kerja. Kita akan menggunakan dua perangkat: Truffle(opens in a new tab) dan Ganache(opens in a new tab).

Truffle adalah lingkungan pengembangan dan kerangka pengujian untuk pengembangan kontrak pintar bagi Ethereum. Dengan menggunakan Truffle, kita mudah membangun dan menyebarkan kontrak pintar ke rantai blok. Ganache membuat kita dapat menciptakan rantai blok Ethereum lokal untuk menguji kontrak pintar. Ganache menyimulasikan fitur dari jaringan yang sebenarnya dan 10 akun pertama didanai dengan 100 ether pengujian, sehingga membuat penyebaran dan pengujian kontrak pintar menjadi gratis dan mudah. Ganache tersedia sebagai aplikasi desktop dan perangkat baris perintah. Untuk artikel ini, kita akan menggunakan aplikasi desktop UI.

Aplikasi desktop UI Ganache(opens in a new tab)Aplikasi desktop UI Ganache

Untuk membuat proyek, jalankan perintah berikut

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

Perintah ini akan membuat sebuah proyek kosong untuk pengembangan dan penyebaran kontrak pintar kita. Struktur proyek yang dibuat adalah sebagai berikut:

  • contracts: Folder untuk kontrak pintar solidity

  • migrations: Folder untuk skrip penyebaran

  • test: Folder untuk menguji kontrak pintar kita

  • truffle-config.js: File konfigurasi Truffle

Buat Token ERC20

Pertama, kita perlu membuat token ERC20 yang akan digunakan untuk melakukan taruhan pada kontrak pintar. Untuk membuat token yang dapat ditukarkan, kita terlebih dahulu perlu menginstal pustaka OpenZeppelin. Pustaka ini memuat penerapan standar seperti ERC20 dan ERC721. Untuk menginstal pustaka tersebut, jalankan perintah:

npm install @openzeppelin/contracts

Dengan menggunakan pustaka OpenZeppelin, kita dapat membuat token ERC20 dengan menulis ke contracts/MyToken.sol dengan kode solidity berikut:

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}
Tampilkan semua
Salin

Dalam kode di atas:

  • Baris 3: Kita mengimpor kontrak ERC20.sol dari openzeppelin yang memuat penerapan untuk standar token ini.

  • Baris 5: Kita mewariskan dari kontrak ERC20.sol.

  • Baris 6: Kita memanggil konstruktor ERC20.sol dan melewatkan parameter nama dan simbol sebagai "MyToken" dan "MTKN" secara berurutan.

  • Baris 7: Kita mencetak dan mentransfer 1 juta token untuk akun yang menyebarkan kontrak pintar (kita menggunakan 18 desimal default untuk token ERC20, yang berarti bahwa jika kita ingin mencetak 1 token, Anda akan menyatakannya sebagai 1000000000000000000, 1 dengan 18 nol).

Kita dapat melihat di bawah penerapan konstruktor ERC20.sol di mana field _decimals ditetapkan menjadi 18:

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}
Tampilkan semua
Salin

Kumpulkan Token ERC20

Untuk mengumpulkan kontrak pintar kita, kita harus terlebih dahulu memeriksa versi pengumpul solidity kita. Anda dapat memeriksa versi tersebut dengan menjalankan perintah:

truffle version

Versi default adalah Solidity v0.5.16. Karena token kita ditulis menggunakan versi solidity 0.6.2, jika kita menjalankan perintah untuk mengumpulkan kontrak kita, maka kita akan mendapatkan kesalahan pada pengumpul. Untuk menentukan versi pengumpul solidity yang digunakan, buka file truffle-config.js dan tetapkan versi pengumpul yang diinginkan seperti terlihat di bawah:

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}
Tampilkan semua

Sekarang, kita dapat mengumpulkan kontrak pintar kita dengan menjalankan perintah berikut:

truffle compile

Sebarkan Token ERC20

Setelah dikumpulkan, kita sekarang dapat menyebarkan token kita.

Pada folder migrasi, buat file yang diberi nama 2_deploy_Tokens.js. File ini adalah tempat kita akan menyebarkan baik Token ERC20 maupun kontrak pintar FarmToken kita. Kode di bawah digunakan untuk menyebarkan kontrak MyToken.sol kita:

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}

Buka Ganache dan pilih opsi "Quickstart" untuk memulai rantai blok Ethereum lokal. Untuk menyebarkan kontrak kita, jalankan:

truffle migrate

Alamat yang digunakan untuk menyebarkan kontrak kita adalah daftar alamat pertama yang ditunjukkan Ganache kepada kita. Untuk memverifikasinya, kita dapat membuka aplikasi desktop Ganache, dan kita dapat memverifikasi bahwa saldo ether karena saldo akun pertama telah berkurang akibat biaya ether untuk menyebarkan kontrak pintar kita:

Aplikasi desktop Ganache(opens in a new tab)Aplikasi desktop Ganache

Untuk memverifikasi bawa 1 juta token MyToken telah dikirimkan ke alamat penyebar, kita dapat menggunakan Konsol Truffle untuk berinteraksi dengan kontrak pintar yang telah disebarkan.

Konsol Truffle adalah konsol interaktif dasar yang terhubung ke klien Ethereum mana pun.(opens in a new tab)

Agar dapat berinteraksi dengan kontrak pintar kita, jalankan perintah berikut:

truffle console

Sekarang, kita dapat menulis perintah berikut dalam terminal:

  • Dapatkan kontrak pintar: myToken = menunggu MyToken.deployed()

  • Dapatkan susunan akun dari Ganache: accounts = menunggu web3.eth.getAccounts()

  • Dapatkan saldo akun pertama: saldo = menunggu myToken.balanceOf(accounts[0])

  • Format saldo dari 18 desimal: web3.utils.fromWei(balance.toString())

Dengan menjalankan perintah di atas, kita akan melihat bahwa alamat pertama sebenarnya memiliki 1 juta MyTokens:

Alamat pertama memiliki 1000000 MyTokens(opens in a new tab)

Alamat pertama memiliki 1000000 MyTokens

Buat Kontrak Pintar FarmToken

Kontrak pintar FarmToken akan memiliki 3 fungsi:

  • balance(): Dapatkan saldo MyToken pada kontrak pintar FarmToken.

  • deposit(uint256 _amount): Transfer MyToken atas nama pengguna ke kontrak pintar FarmToken, kemudian cetak dan transfer FarmToken ke pengguna tersebut.

  • withdraw(uint256 _amount): Bakar FarmToken pengguna dan transfer MyToken ke alamat pengguna tersebut.

Mari kita lihat konstruktor FarmToken:

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 }
Tampilkan semua
Salin
  • Baris 3-6: Kita mengimpor kontrak berikut dari openzeppelin: IERC20.sol, Address.sol, SafeERC20.sol, dan ERC20.sol.

  • Baris 8: FarmToken akan mewarisi dari kontrak ERC20.

  • Baris 14-19: Konstruktor FarmToken akan diterima sebagai parameter alamat kontrak MyToken dan kita akan menugaskan kontrak tersebut ke variabel publik kita yang disebut token.

Mari kita terapkan fungsi balance(). Fungsi ini tidak akan menerima parameter dan akan mengembalikan saldo MyToken pada kontrak pintar ini. Fungsi ini diterapkan seperti terlihat di bawah:

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

Untuk fungsi deposit(uint256 _amount), fungsi ini akan diterima sebagai parameter jumlah yang ingin disetor pengguna dan fungsi ini akan mencetak dan mentransfer FarmToken ke pengguna:

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}
Tampilkan semua
Salin

Untuk fungsi withdraw(uint256 _amount), kita akan menerima fungsi tersebut sebagai parameter jumlah FarmToken yang ingin di bakar oleh pengguna dan kemudian mentransfer kembali jumlah MyToken yang sama ke pengguna:

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}
Salin

Sekarang, kita akan menyebarkan kontrak pintar kita. Untuk melakukannya, kita akan kembali ke file 2_deploy_Tokens.js dan menambahkan kontrak baru yang akan disebarkan:

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}
Tampilkan semua

Perlu diperhatikan bahwa saat menyebarkan FarmToken, kita menyebarkan alamat kontrak MyToken yang digunakan sebagai parameter.

Sekarang, jalankan truffle compile dan truffle migrate untuk menyebarkan kontrak kita.

Mari kita menguji kontrak pintar kita. Alih-alih menggunakan konsol truffle untuk berinteraksi dengan kontrak pintar kita, kita akan membuat skrip untuk mengautomasi proses ini. Buat folder bernama skrip dan tambahkan file getMyTokenBalance.js berikut. Folder ini akan memeriksa saldo MyToken pada kontrak pintar FarmToken:

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}
Tampilkan semua

Untuk melaksanakan skrip, jalankan perintah cli berikut:

truffle exec .\scripts\getMyTokenBalance.js

Kita akan mendapatkan hasil yang diinginkan, yaitu 0. Jika Anda mendapatkan kesalahan tentang FarmToken yang belum disebarkan, jaringan truffle belum menerima versi terbaru dari kode kontrak Anda. Cukup tutup ganache, mulai cepat lagi dan pastikan untuk menjalankan truffle migrate.

Sekarang, mari kita bertaruh MyToken ke kontrak pintar. Karena fungsi deposit(uint256 _amount) memangil fungsi safeTransferFrom dari ERC20, pengguna harus terlebih dahulu menyetujui kontrak pintar untuk mentransfer MyToken atas nama pengguna. Jadi, pada skrip di bawah, kita akan terlebih dahulu menyetujui langkah ini, kemudian kita akan memanggil fungsi:

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}
Tampilkan semua

Untuk menjalankan skrip ini: truffle exec .\scripts\transferMyTokenToFarmToken.js. Anda seharusnya melihat pada konsol Anda:

output dari transferMyTokenToFarmToken.js(opens in a new tab)

output dari transferMyTokenToFarmToken.js

Seperti yang dapat kita lihat, kita telah berhasil menyetor MyToken ke kontrak pintar karena akun pertama sekarang memiliki FarmToken.

Untuk melakukan penarikan:

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}
Tampilkan semua

Untuk menjalankan skrip ini: truffle exec .\scripts\withdrawMyTokenFromTokenFarm.js. Seperti yang dapat kita lihat pada output di bawah, kita telah berhasil mendapatkan MyToken kembali dan kita telah membakar FarmToken:

output dari withdrawMyTokenFromTokenFarm.js(opens in a new tab)

output dari withdrawMyTokenFromTokenFarm.js

Referensi

Kontrak - Dokumentasi OpenZeppelin(opens in a new tab)

Perangkat Berguna untuk Kontrak Pintar | Truffle Suite(opens in a new tab)

Ganache | Truffle Suite(opens in a new tab)

Apa yang Dimaksud dengan DeFi? Panduan bagi Pemula (Diperbarui Tahun 2021) (99bitcoins.com)(opens in a new tab)

DeFi - Papan Peringkat Keuangan Terdesentralisasi di DeFi Llama(opens in a new tab)

Terakhir diedit: @yeremiaryangunadi(opens in a new tab), 15 Agustus 2023

Apakah tutorial ini membantu?