Cách tạo và triển khai token ERC-20 trên blockchain Ethereum

Với tốc độ phát triển hiện nay, blockchain đang dần chiếm lĩnh thế giới. Bitcoin, tiền điện tử phổ biến nhất, là sản phẩm của công nghệ blockchain đầu tiên và nổi tiếng nhất thế giới. Ethereum là một sản phẩm thứ cấp của blockchain và ra đời khi Bitcoin bắt đầu phát triển mạnh.

Ethereum

Trong khi Bitcoin chỉ phát triển theo hướng trở thành một loại tiền tệ, Ethereum thậm chí tiến xa hơn với tầm nhìn cung cấp máy ảo (EVM) và các hợp đồng thông minh cho phép người dùng tạo ra các token trong mạng lưới.

Token có thể đại diện cho tiền tệ, vàng bạc, vé xổ số… Bạn có thể phát triển tất cả các loại token trên blockchain Ethereum, nhưng hướng dẫn này sẽ tập trung vào cách tạo và triển khai token ERC-20.

Trước khi tạo token, bạn phải có ít nhất kiến thức cơ bản về công nghệ blockchain, ngôn ngữ Solidity và cách Ethereum hoạt động.

Blockchain là gì và cách thức hoạt động như thế nào?

Nói một cách dễ hiểu, blockchain là bản ghi các giao dịch trong một sổ cái hoặc cơ sở dữ liệu được phân phối cho nhiều người tham gia mạng. Sổ cái này chứa bản ghi của các giao dịch được thực hiện trong mạng.

Giao dịch là quá trình chuyển một loại tiền tệ hoặc một lượng tiền nhất định từ người dùng này sang người dùng khác trong mạng. Ví dụ, giả sử Alice chuyển 30BLC cho Bob. Giao dịch này được hash bằng mật mã và được một node nhất định trên mạng ghi lại vào sổ cái.

Node này gửi giao dịch đến các node khác trong mạng – tức là, nó truyền giao dịch tới mạng. Các node khác nhận giao dịch, xác minh bằng phương pháp tiêu chuẩn, sau đó thêm vào sổ cái.

Các node trong mạng tiếp nhận giao dịch mới được truyền trong mạng, sau đó thêm giao dịch vào sổ cái. Mỗi node trong mạng sở hữu hoặc có một bản sao của sổ cái. Chính điều này đã tạo ra bản chất phân tán của blockchain.

Từ “blockchain” có nguồn gốc từ việc các giao dịch hoặc bản ghi được liên kết thành một chuỗi bên trong sổ cái. Như chúng ta đã biết, giao dịch đại diện cho quá trình trao đổi tiền tệ giữa hai bên trong node, có thể được biểu diễn bằng JSON như sau:

{

“to”: “0xalice”,

“from”: “0xbob”,

“amount”: “30BLC”

}

Đây là một bản ghi hoặc giao dịch đơn giản. Nó cho biết Bob đã chuyển 30BLC cho Alice.

Giao dịch này được ghi lại bên trong một khối, được viết với định dạng dữ liệu JSON như sau:

[

{

“to”: “0xalice”,

“from”: “0xbob”,

“amount”: “30BLC”

}

]

Một khối giống như một dãy chứa nhiều đối tượng của các giao dịch. Vì vậy, khối này có thể chứa nhiều giao dịch:

[

{

“to”: “0xalice”,

“from”: “0xbob”,

“amount”: “30BLC”

},

{

“to”: “0xtheresa”,

“from”: “0xarinze”,

“amount”: “5BLC”

}

]

Bạn có thể thấy vị trí của khối thêm giao dịch vào. Chuỗi được tạo thành từ các khối liên kết với nhau. Mỗi blockchain bắt đầu với một khối gốc (genesis block) mà người tạo thêm vào và truyền tới mạng.

Mỗi khối cũng có một hash mật mã, đóng vai trò như một số định danh duy nhất trong mạng. Không có hai khối nào có cùng một hash.

Khi một khối được các node xác minh và thêm vào mạng, nó có một con trỏ chỉ đến hash của khối cuối cùng trong mạng.

[

{

“hash”: “0x0”,

“prevHash”: “”,

“txns”: [

{

“to”: “0xalice”,

“from”: “0xbob”,

“amount”: “30BLC”

},

{

“hash”: “0x1”,

“prevHash”: “0x0”,

“to”: “0xtheresa”,

“from”: “0xarinze”,

“amount”: “5BLC”

}

]

},

{

“hash”: “0x1”,

“prevHash”: “0x0”,

“txns”: [

{

“to”: “0xalice”,

“from”: “0xbob”,

“amount”: “30BLC”

},

{

“to”: “0xtheresa”,

“from”: “0xarinze”,

“amount”: “5BLC”

}

]

}

]

Khối đầu tiên có hash 0x0 là khối gốc. Khối tiếp theo với hash 0x1 có một prevHash chỉ đến 0x0, hay nói cách khác là chỉ đến khối đầu tiên trong mạng.

Bằng cách này, mỗi khối mới được hợp nhất vào mạng đều chỉ đến khối mới nhất liền kề trước. Đây là cách hình thành “chuỗi” trong blockchain.

Ethereum là gì?

Ethereum là một blockchain có tiền kỹ thuật số riêng, được gọi là ETH. Cũng giống như blockchain khác, các giao dịch được lưu trữ trong sổ cái.

Điều làm cho Ethereum trở nên khác biệt với các blockchain khác là tính linh hoạt. Trong khi nhiều nền tảng blockchain chỉ hỗ trợ chuyển tiền, Ethereum cho phép chuyển mọi dữ liệu thông qua blockchain và trả phí bằng ETH.

Ethereum hoạt động như thế nào?

Như đã đề cập ở trên, trong blockchain Ethereum, chúng ta có thể chuyển bất kỳ dữ liệu nào và trả phí bằng ETH.

Cũng giống như giao dịch BLC trong ví dụ trên, blockchain Ethereum hỗ trợ các giao dịch ETH. Giả sử Alice chuyển 1 ETH cho Bob. Giao dịch này được các node trong mạng xác thực và thêm vào khối trong blockchain.

Ngoài ra, Ethereum còn có hoạt động khai thác, đòi hỏi phải làm việc để có ETH. Công việc này yêu cầu giải quyết một phép tính khó bằng cách thử nhiều cách khác nhau cho đến khi tìm được đáp án đúng. Bất kỳ node nào trong mạng cũng có thể tham gia. Một node giải quyết phép tính thành công sẽ được thưởng lượng ETH nhất định. Độ khó của phép tính tăng lên khi có nhiều giao dịch được khai thác hơn.

Bất cứ khi nào giao dịch được kích hoạt trong blockchain Ethereum, một node khai thác trong mạng sẽ khai thác giao dịch đó. Người gửi giao dịch phải đồng ý trả một số ETH nhất định cho node đó. Đây được gọi là giá gas.

Hợp đồng thông minh là gì?

Hợp đồng thông minh là một công cụ chứa code được thực thi trong blockchain Ethereum. Hợp đồng thông minh được viết bằng ngôn ngữ Solidity và biên dịch thành code ABI. Code ABI này được triển khai vào blockchain Ethereum. Hợp đồng thông minh lấy địa chỉ thuộc sở hữu bên ngoài của người gửi trộn với nonce (số chỉ sử dụng một lần) để tạo thành địa chỉ trong blockchain Ethereum.

Hợp đồng thông minh cho phép tạo các hợp đồng kỹ thuật số. Giống như hợp đồng trong thế giới thực, hợp đồng kỹ thuật số giúp thiết lập giao dịch giữa hai hoặc nhiều bên trong blockchain Ethereum.

Hợp đồng thông minh là một loại tài khoản trong Ethereum, có nghĩa là nó không bị người dùng kiểm soát và có thể gửi các giao dịch trong blockchain. Vì là một tài khoản nên hợp đồng thông minh có số dư và chứa code EVM.

Token ERC-20 là gì?

Ethereum Virtual Machine (EVM) là một máy ảo chạy code Solidity ABI đã biên dịch. Các hợp đồng thông minh trong Ethereum đã trở thành tiêu chuẩn toàn cầu để tạo một số token. Các tiêu chuẩn này được gọi là tiêu chuẩn Ethereum Request for Comment (ERC).

Ethereum có nhiều tiêu chuẩn, nhưng phổ biến và sử dụng rộng rãi nhất là ERC-20 và ERC-721. ERC-20 được sử dụng để tạo token trong khi ERC-721 để phát triển NFT.

ERC-20 là tiêu chuẩn do Fabian Vogelsteller đề xuất, là một hợp đồng thông minh có chứa tập hợp các API. ERC20 là bộ quy tắc áp dụng cho tất cả các token chọn tiêu chuẩn ERC-20.

Như đã đề cập ở trên, ERC-20 có thể được sử dụng để tạo ra các loại tiền ảo như Bitcoin và ETH. Một trong các token nổi tiếng nhất được xây dựng bằng tiêu chuẩn ERC-20 là Binance Coin (BNB) và Shiba Shabu (KOBE).

Người dùng có thể gửi và nhận các token ERC-20. Những token này thuộc loại có thể thay thế, có nghĩa là giá trị của chúng giống nhau ở mọi nơi trong blockchain.

Theo Blockchain.com, các ví và sàn giao dịch sử dụng tiêu chuẩn này để tích hợp nhiều loại token ERC-20 khác nhau vào nền tảng và tạo điều kiện trao đổi chúng cũng như nhiều loại tiền điện tử khác.

Sau khi đã hiểu tiêu chuẩn ERC-20 là gì, hãy cùng xem xét nội dung của token ERC-20.

Nội dung của token ERC-20

Token ERC-20 chứa các phương thức và sự kiện mà một token ERC-20 phải có.

Token ERC-20 phải có khả năng:

  • Chuyển token từ tài khoản này sang tài khoản khác
  • Trả lại số dư của tài khoản
  • Trả lại tổng số token có sẵn bằng token
  • Chuyển token vào tài khoản

Trên thực tế, ERC-20 sẽ trông giống như thế này nếu viết bằng ngôn ngữ Solidity:

function name() public view returns (string)

function symbol() public view returns (string)

function decimals() public view returns (uint8)

function totalSupply() public view returns (uint256)

function balanceOf(address _owner) public view returns (uint256 balance)

function transfer(address _to, uint256 _value) public returns (bool success)

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)

function approve(address _spender, uint256 _value) public returns (bool success)

function allowance(address _owner, address _spender) public view returns (uint256 remaining)

Một token ERC-20 có thể có các phương thức sau đây:

  • name – trả về tên của token (ví dụ: Binance Coin)
  • symbol – trả về biểu tượng của token (ví dụ: BNB)
  • decimals – trả về số thập phân mà token sử dụng
  • totalSupply – trả về tổng số nguồn cung ban đầu của token
  • balanceOf – trả về số dư của tài khoản
  • transfer – chuyển một lượng token nhất định đến một địa chỉ
  • transferFrom – chuyển một lượng token nhất định từ địa chỉ người thụ hưởng đến địa chỉ người nhận
  • approve – rút token từ địa chỉ của chủ sở hữu lên đến một lượng token nhất định
  • allowance – trả lại số lượng token có thể rút từ tài khoản của chủ sở hữu

Các sự kiện cũng có thể được đăng ký trên token để kịp thời nắm bắt khi có tín hiệu phát ra. Token ERC-20 có các sự kiện sau:

event Transfer(address indexed _from, address indexed _to, uint256 _value)

event Approval(address indexed _owner, address indexed _spender, uint256 _value)

  • Transfer – kích hoạt khi chuyển token
  • Approval – kích hoạt khi tài khoản được chấp thuận thu nhận số lượng token nhất định

Tạo token ERC-20

Để dễ hiểu hơn ở phần này, bài viết sẽ trình bày cách viết một token đơn giản và gọi nó là ND Coin.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract NDCoinERC20 {

event Transfer(address indexed from, address indexed to, uint tokens);

event Approval(address indexed tokenOwner, address indexed spender, uint tokens);

string public constant name = “ND Coin”;

string public constant symbol = “NDN”;

uint8 public constant decimals = 18;

mapping(address => uint256) balances;

mapping(address => mapping (address => uint256)) allowed;

uint256 totalSupply_;

constructor(uint256 total) {

totalSupply_ = total;

balances[msg.sender] = totalSupply_;

}

function totalSupply() public view returns (uint256) {

return totalSupply_;

}

function balanceOf(address tokenOwner) public view returns (uint) {

return balances[tokenOwner];

}

function transfer(address receiver, uint numTokens) public returns (bool) {

require(numTokens <= balances[msg.sender]);

balances[msg.sender] -= numTokens;

balances[receiver] += numTokens;

emit Transfer(msg.sender, receiver, numTokens);

return true;

}

function approve(address delegate, uint numTokens) public returns (bool) {

allowed[msg.sender][delegate] = numTokens;

emit Approval(msg.sender, delegate, numTokens);

return true;

}

function allowance(address owner, address delegate) public view returns (uint) {

return allowed[owner][delegate];

}

function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) {

require(numTokens <= balances[owner]);

require(numTokens <= allowed[owner][msg.sender]);

balances[owner] -= numTokens;

allowed[owner][msg.sender] -= numTokens;

balances[buyer] += numTokens;

emit Transfer(owner, buyer, numTokens);

return true;

}

}

Với đoạn code trên, chúng ta đã viết xong tiền điện tử của riêng mình bằng cách sử dụng tiêu chuẩn ERC-20. Có thể giải thích tất cả các phương thức trên được triển khai bằng tiêu chuẩn ERC-20 như sau:

Dòng code đầu tiên thiết lập số nhận dạng giấy phép và phiên bản Solidity mà code viết. Ở đây, code Solidity là Solidity v0.7.0–0.9.0.

Hai dòng tiếp theo khai báo các sự kiện Transfer và Approval:

event Transfer(address indexed from, address indexed to, uint tokens);

event Approval(address indexed tokenOwner, address indexed spender, uint tokens);

Tiếp theo, code đặt tên token, ký hiệu và số thập phân để sử dụng:

string public constant name = “ND Coin”;

string public constant symbol = “NDN”;

uint8 public constant decimals = 18;

Tên token trong ví dụ này là ND Coin và có biểu tượng NDN.

Tiếp theo, chúng ta có hai mapping được khai báo:

mapping(address => uint256) balances;

mapping(address => mapping (address => uint256)) allowed;

Một mapping trong Solidity tương tự như một cặp khóa-giá trị. Vì vậy, trong balances (số dư), address là khóa trong khi unit256 (số nguyên không dấu của 256 bit) là giá trị của khóa đó.

Theo tài liệu của Solidity, kiểu address là giá trị 160 bit không cho phép thực hiện mọi phép toán số học. Nó chỉ phù hợp để lưu trữ địa chỉ của các hợp đồng hoặc hash của một nửa cặp khóa công khai thuộc về tài khoản bên ngoài.

Balances ánh xạ (map) một địa chỉ tới một số nguyên unit256:

Địa chỉ unit256
0x01 23
0x02 10
0x03 2

Mỗi địa chỉ có một số dư riêng. Mapping allowed cũng là một cặp khóa-giá trị ánh xạ địa chỉ tới một mapping khác. Mapping cuối cùng này ánh xạ địa chỉ đến các giá trị unit256 của chúng. Theo đó, bạn có thể lưu trữ số lượng token được chuyển cho người nhận.

Dòng mã tiếp theo như sau:

uint256 totalSupply_;

Dòng này lưu trữ số lượng token có sẵn trong hợp đồng.

Tiếp theo, chúng ta có constructor. Thông thường, các constructor (hàm tạo) phát huy tác dụng khi đang tạo lớp (class). Trong hợp đồng thông minh, constructor được sử dụng khi triển khai hợp đồng vào mạng.

constructor(uint256 total) {

totalSupply_ = total;

balances[msg.sender] = totalSupply_;

}

Ở đây, constructor được sử dụng với tổng số token muốn có trong hợp đồng (total). Total được đặt thành totalSupply_ và số dư của địa chỉ triển khai được đặt thành tổng số token. Msg.sender chứa tài khoản Ethereum mà hàm hợp đồng hiện đang thực thi.

Trong dòng tiếp theo, chúng ta có phương thức balanceOf:

function balanceOf(address tokenOwner) public view returns (uint) {

return balances[tokenOwner];

}

Phương thức balanceOf có một đối số là tokenOwner. Đối số này là địa chỉ của chủ sở hữu token mà ví dụ muốn trả lại số dư token trong hợp đồng. Vì vậy, phương thức lấy số dư bằng cách tham chiếu từ balances của địa chỉ tokenOwner.

Phương thức tiếp theo là transfer:

function transfer(address receiver, uint numTokens) public returns (bool) {

require(numTokens <= balances[msg.sender]);

balances[msg.sender] -= numTokens;

balances[receiver] += numTokens;

emit Transfer(msg.sender, receiver, numTokens);

return true;

}

Phương thức này có các đối số:

  • receiver là địa chỉ của tài khoản nhận token
  • numTokens là số lượng token được gửi đến tài khoản receiver

Trong phần nội dung của phương thức, kiểm tra được thực hiện để xác minh đủ số lượng token được gửi đến người nhận theo số dư trong địa chỉ của người triển khai.

Tiếp theo, numTokens được trừ vào số dư của người triển khai và được ghi có vào số dư của receiver. Sau đó, sự kiện Transfer được phát ra. Cuối cùng, trả về true theo kiểu dữ liệu Boolean.

Phương thức tiếp theo là approve:

function approve(address delegate, uint numTokens) public returns (bool) {

allowed[msg.sender][delegate] = numTokens;

emit Approval(msg.sender, delegate, numTokens);

return true;

}

Phương thức này có các đối số delegate và numTokens.

  • delegate là địa chỉ nhận số lượng token mà người triển khai gửi đến
  • numTokens là số lượng token mà người triển khai có thể gửi cho delegate

Trong phần nội dung phương thức, ví dụ tham chiếu map delegate trong mapping allowed để đặt số lượng token cho nó. Sau đó, code phát ra sự kiện Approval và trả về true.

Phương thức tiếp theo là allowance:

function allowance(address owner, address delegate) public view returns (uint) {

return allowed[owner][delegate];

}

Phương thức này có các đối số: owner và delegate. owner là địa chỉ để trả lại số lượng token có thể chuyển nhượng cho người nhận trong delegate.

Phương thức cuối cùng là transferFrom:

function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) {

require(numTokens <= balances[owner]);

require(numTokens <= allowed[owner][msg.sender]);

balances[owner] -= numTokens;

allowed[owner][msg.sender] -= numTokens;

balances[buyer] += numTokens;

emit Transfer(owner, buyer, numTokens);

return true;

}

transferFrom có các đối số owner, buyer và numTokens.

  • owner là địa chỉ có số dư mà sẽ chuyển numTokens từ đó
  • buyer là địa chỉ sẽ ghi có numTokens
  • numTokens là số lượng token được chuyển từ owner sang buyer

Trong phần nội dung phương thức, trước tiên cần kiểm tra xem số dư trong địa chỉ của chủ sở hữu có đủ không và liệu chủ sở hữu có được chấp thuận để gửi số lượng token đó cho người mua không.

Tiếp theo, quá trình chuyển token được thực hiện bằng cách trừ số lượng token từ số dư của chủ sở hữu và số dư được phép. Sau đó, số lượng token này được thêm vào số dư của người mua. Sự kiện Transfer được phát ra và trả về true theo kiểu dữ liệu Boolean.

Triển khai token trên testnet Ethereum

Tiếp theo, hãy thử triển khai hợp đồng với mạng Ethereum – thực ra không phải là mạng Ethereum, mà là một mạng thử nghiệm (testnet) của Ethereum. Chúng ta không thể triển khai hợp đồng trên mạng Ethereum thực vì cần phải có chi phí. Vì ví dụ chỉ đang minh họa phương thức thực hiện nên sẽ sử dụng testnet này và chuyển ETH miễn phí để triển khai hợp đồng.

Token đã tạo ở trên sẽ được triển khai vào testnet Ropsten và biên dịch hợp đồng thông minh trên Remix (một trình biên dịch trực tuyến cho Solidity). Tiếp theo, sử dụng MetaMask để tạo ví trên testnet Ethereum.

Cài đặt tiện ích mở rộng MetaMask trên trình duyệt máy tính. Tạo một tài khoản.

Tiếp theo, vào Remix và tạo tệp .sol mới. Trong ví dụ này, tệp được đặt tên là nd_coin.sol và dán hợp đồng thông minh trong phần trên vào đó:

token erc 20 Ethereum

Lúc này, chúng ta sẽ nhận được một số ETH miễn phí.

Nếu mở tiện ích mở rộng MetaMask, chúng ta sẽ thấy có 0 ETH, nhưng có thể nhận ETH miễn phí từ mạng Ropsten. Hãy sao chép địa chỉ tài khoản của mình từ MetaMask, sau đó dán vào trang https://faucet.ropsten.be và nhấp “Send me test Ether”.

token erc 20 Ethereum

Yêu cầu sẽ được đưa vào hàng đợi và sau khoảng 3 phút, có 0,3 ETH trong tài khoản MetaMask.

token erc 20 Ethereum

Bây giờ, chúng ta có thể triển khai hợp đồng thông minh với testnet Ropsten.

Quay lại bảng điều khiển Remix và nhấp vào biểu tượng Ethereum trong bảng điều khiển. Thao tác này sẽ tải một trang để triển khai và chạy các giao dịch.

Ở thanh bên bên trái, hợp đồng nd_coin.sol đã được chọn và tài khoản trong MetaMask được đặt làm địa chỉ triển khai. Trong ENVIRONMENT, JavaScript VM (London) được chọn sẵn.

token erc 20 Ethereum

Hãy thay đổi nó để mạng triển khai sẽ là mạng Ropsten. Nhấp vào menu thả xuống và chọn Injected Web3. Theo đó, có 0,3 ETH trong phần ACCOUNT ngay bên cạnh địa chỉ triển khai. Bây giờ đã sẵn sàng triển khai hợp đồng thông minh.

Hãy nhập số lượng token ban đầu được giao dịch vào ô bên cạnh nút Deploy. Nhập 100 và nhấp vào nút Deploy (triển khai).

Thao tác này sẽ mở tiện ích mở rộng MetaMask và yêu cầu xác nhận giao dịch đang chờ xử lý. Nhấp vào nút Confirm trên cửa sổ MetaMask bật lên.

Thao tác này sẽ triển khai hợp đồng thông minh trên testnet Ropsten.

Tiếp theo, cuộn xuống trên thanh bên trái của trang Remix sẽ thấy tên của các phương thức trong hợp đồng thông minh và một ô đầu vào bên cạnh chúng.

Đây là nơi có thể chạy các phương thức trong hợp đồng thông minh và nhận được kết quả.

Như bạn có thể thấy bên dưới, ví dụ đã kiểm tra số dư của địa chỉ triển khai và nó trả về 100:

Như vậy, chúng ta đã tạo và triển khai thành công token ERC-20 trên mạng Ethereum.

Kết luận

Tóm lại, hướng dẫn này đã cung cấp cho bạn những kiến thức cơ bản nhất về blockchain, Ethereum và sau đó đi sâu vào ERC-20. Bài viết phân tích nội dung ERC-20 và trình bày chi tiết về các phương thức, sự kiện trong token ERC-20. Để dễ hiểu hơn, hướng dẫn cũng thử tạo một token đơn giản bằng ngôn ngữ Solidity và triển khai vào testnet Ropsten.

Còn rất nhiều điều cần khám phá, nhưng hy vọng hướng dẫn sẽ giúp bạn có một khởi đầu thuận lợi khi bước vào thế giới của token ERC.

Đình Đình

Theo Logrocket

Theo dõi trang Twitter | Theo dõi kênh Telegram | Theo dõi trang Facebook