Tất tần tật cách tạo và deploy Token cùng với DApp

Trần Đình Quý

Updated on:

token

Tiền điện tử là một dạng tiền tệ kỹ thuật số được tạo ra và quản lý bằng cách sử dụng công nghệ mã hóa, đặc biệt là công nghệ blockchain. Điểm đặc trưng của tiền điện tử là không tồn tại dưới dạng vật chất như tiền mặt, mà thay vào đó, chúng tồn tại dưới dạng dữ liệu kỹ thuật số được lưu trữ và truyền tải qua mạng internet.

1. Cách tạo 1 token của riêng mình

Trước tiên chúng ta cần phải định nghĩa được Coin là gì và Token là gì?

1.1 Coin:

Coin hay còn được biết đến với các tên gọi khác như Altcoin, Cryptocurrency Coin. Đây là một dạng tiền kỹ thuật số, được coi tương đương với tiền tệ. Nó được tạo bởi kỹ thuật mã hóa, lưu trữ giá trị theo thời gian, chia sẻ giữa những người tham gia trên mạng lưới blockchain.

Coin sở hữu các tính năng như đơn vị thanh toán, trao đổi, nhận thưởng, bảo mật, quản trị,… của chính blockchain phát hành. Mỗi một blockchain chỉ có 1 loại coin cơ bản duy nhất trong toàn mạng như blockchain của Bitcoin có đồng coin là BTC, EthereumETH, LitecounLTC, các đồng coin khác sẽ được tạo ra dựa vào các coin này.

Tuy nhiên, coin là gì cũng có một số cách hiểu khác. Một số người cho rằng nó ám chỉ các đồng token được niêm yết trên sàn giao dịch tập trung (CEX). Đây chính là điểm dễ gây nhầm lẫn giữa Coin và Token

1.2 Token:

Trong thị trường crypto, token là đồng tiền điện tử được phát hành và hoạt động dựa trên công nghệ blockchain. Một dự án có thể phát hành token trên nhiều nền tảng blockchain khác nhau.

Vậy điều khác biệt cơ bản ở đây là Coin là một loại tiền điện tử độc lập, tồn tại trên một blockchain riêng và Token là một loại tiền điện tử được xây dựng trên một blockchain hiện có, thường là Ethereum.

1.3 Cách tạo 1 token

Trong bài viết này tôi sẽ tạo 1 token có tên là Tora và ký hiệu là TR1, token này sẽ được viết trên nền tảng Etherium, theo chuẩn ERC-20.

Để tạo token này chúng ta cần 1 công cụ là Remix IDE, một IDE để phát triển các token theo theo ngôn ngữ solidity. Là 1 IDE trên nền tàng web nên các bạn chỉ cần nhấn vào đây để truy cập.

Ở thư mục contracts, chúng ta sẽ tạo 1 file bất kì, như ví dụ dưới đây sẽ là TRT_coin.sol. Và nhập đoạn code phía dưới vào:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

Giải thích một chút về đoạn code này thì chúng ta sẽ sử dụng solidity phiên bản 0.8.0 trở lên.
Interface IERC20 là interface có sẵn mà mỗi token theo chuẩn này phải có, và được định nghĩa bao gồm các function và event như trên.

Tiếp tục dưới đoạn code ở trên, chúng ta sẽ implement interface IERC20.

contract TRTCoin is IERC20 {
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 private _totalSupply;
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;

    constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 initialSupply) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
        _totalSupply = initialSupply * (10 ** uint256(decimals));
        _balances[msg.sender] = _totalSupply;
        emit Transfer(address(0), msg.sender, _totalSupply);
    }

    function totalSupply() external view override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) external view override returns (uint256) {
        return _balances[account];
    }

    function transfer(address recipient, uint256 amount) external override returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) external view override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) external override returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, msg.sender, _allowances[sender][msg.sender] - amount);
        return true;
    }

    function _transfer(address sender, address recipient, uint256 amount) private {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(_balances[sender] >= amount, "ERC20: transfer amount exceeds balance");

        _balances[sender] -= amount;
        _balances[recipient] += amount;
        emit Transfer(sender, recipient, amount);
    }

    function _approve(address owner, address spender, uint256 amount) private {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
}

Chúng ta sẽ có 1 constructor function để tạo token, trong đó sẽ định nghĩa tên token, ký hiệu của token, đơn vị 10^x, số lượng ban đầu.
Các funcion cơ bản sẽ là:

  • totalSupply: Tổng cung của token
  • balanceOf: Tài khoản của 1 địa chỉ ví
  • transferFrom: Chuyển token từ ví này sang ví khác
  • allowance: Kiểm tra xem có quyền chuyển một số lượng token nhất đinh từ 1 ví sang ví khác.

Chúng ta sẽ thực hiện compile file TRT_Coin.sol này bằng cách nhấn vào nút Compile TRT_Coin.sol như hình bên dưới.

Bây giờ chúng ta sẽ run thử xem đoạn code của chúng ta đã chạy ổn chưa bằng cách nhấn nút Deploy.

Ở đây vì chúng ta chạy thử nên hãy chọn Environment là Remix VM (…) nhé. Ở mục contract, chọn class TRTCoin. Các thông tin khác của token sẽ ở mục Deploy.

1.4 Test thử 1 vài giao dịch

Sau khi đã run được token lên, ta sẽ phải test các function trên xem đã hoạt động đúng chưa.

Đầu tiên sẽ là totalSupply, tức tổng cung của token.

Tiếp theo sẽ kiểm tra tài khoản, mặc định token được tạo ra sẽ nằm ở địa chỉ ví tạo ra token đó, như ở đây. Hãy copy và add vào phần balanceOf, tức tài khoản của 1 ví.

Tiếp tục sẽ kiểm tra việc chuyển token từ tài khoản này sang tài khoản khác bằng function transfer.
Ở chúng ta sẽ nhập địa chỉ ví người nhận ở phần recipient, số lượng muốn gửi và nhấn transact. Địa chỉ ví ảo có thể lấy ngay phần account phía trên với account khác.

Và chúng ta hãy kiểm tra lại tài khoản của tài khoản mà chúng ta đã gửi.

Có vẻ như các function đều hoạt động tốt, các bạn có thể kiểm tra các function còn lại với cách tương tự.

2. Deploy

Sau khi kiểm tra thành công, chúng ta sẽ deploy chúng lên để có thể sử dụng được. Để phục vụ cho quá trình kiểm thử thì thay vì mainnet, Etherium đã tạo ra các testnet để chúng ta thực hiện việc kiểm thử trước khi đưa vào môi trường production. Một trong số đó mà chúng ta sử dụng sẽ là Sepolia.

Để thực hiện việc deploy lên testnet, trước hết ta phải tạo 1 ví metamask và thêm vào trình duyệt. Nhớ chuyển về Sepolia nhé.

Quay trở lại RemixIDE, tương tự với việc run token để test như phần trước. Nhưng sẽ có 1 vài thay đổi.

Ở phần Environment, chọn Inject Provider – MetaMask.

Sau đó deploy với các đầu vào như bình thường. Đợi một chút và thành quả sẽ đến.

Lúc này, khi vào Etherscan của sepolia, thông tin token ta deploy đã xuất hiện.

Lúc này bạn hãy lưu lại token contract để sử dụng ở bước tiếp theo.

Và chúng ta cũng cần lưu lại ABI có thể lấy ở trên RemixIDE.

3. Tạo DApp

DApp là viết tắt của cụm từ “Decentralized Application” (Ứng dụng phi tập trung). Đây là một loại ứng dụng mà hoạt động trên một mạng lưới phi tập trung như blockchain. Dapp sử dụng các hợp đồng thông minh để thực hiện các chức năng và lưu trữ dữ liệu trên blockchain, thay vì dựa vào một máy chủ trung tâm hoặc cơ sở dữ liệu truyền thống.

Chúng ta sẽ sử dụng Flutter để viết 1 ứng dụng DApp kết nối với token chúng ta đã tạo.

Hãy tạo 1 ứng dụng Flutter như bình thường bằng VSCode hoặc Android Studio.
Cài đặt thư viện web3model_flutter bằng cách chạy câu lệnh sau:

flutter pub add web3modal_flutter

3.1 Cài đặt deeplink

Chúng ta sử dụng App liên kết với ví Metamask, vì thế cần phải di chuyển qua lại giữa App và Metamask với nhau, điều đó được thực hiện thông qua deeplink.

iOS

  • Tìm tệp Info.plist của bạn trong thư mục your_project/ios/Runner/.
  • Tìm phần CFBundleURLTypes.
  • Thêm schema của bạn như một mục trong đối tượng như sau:
  <key>CFBundleURLTypes</key>
  <array>
    <dict>
      <key>CFBundleTypeRole</key>
      <string>Editor</string>
      <key>CFBundleURLName</key>
      <string>com.example.yourBundleId</string> <!-- Bundle ID of your app -->
      <key>CFBundleURLSchemes</key>
      <array>
        <!-- your own custom scheme. Be mind of removing :// for this step -->
        <string>flutterdapp</string>
      </array>
    </dict>
  </array>

Android

  • Tìm tệp AndroidManifest.xml của bạn trong thư mục your_project/android/app/src/main/.
  • Thêm intent sau:
  <intent-filter>
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <!-- your own custom scheme. Be mind of removing :// for this step -->
      <data android:scheme="flutterdapp" />
  </intent-filter>

3.2 Khởi tạo Service


Hãy sử dụng đoạn code sau để khởi tạo service. Hãy để nó ở phần initState hoặc init service như một singleton tuỳ theo cấu hình của app bạn.

  final _w3mService = W3MService(
    projectId: '{YOUR_PROJECT_ID}',
    metadata: const PairingMetadata(
      name: 'Web3Modal Flutter Example',
      description: 'Web3Modal Flutter Example',
      url: 'https://www.walletconnect.com/',
      icons: ['https://walletconnect.com/walletconnect-logo.png'],
      redirect: Redirect(
        native: 'flutterdapp://',
        universal: 'https://www.walletconnect.com',
      ),
    ),
  );

  // Register callbacks on the Web3App you'd like to use. See `Events` section.

  await _w3mServices.init();

Để lấy project Id, các bạn vào đây để đăng kí tài khoản vào tạo 1 project, sau đó lấy project Id nhé.

3.3 Thêm mạng Sepolia

Khi chúng ta thực hiện test, vì mạng Sepolia sẽ không có sẵn nên chúng ta phải thêm vào.

W3MChainPresets.chains.putIfAbsent('11155111', 
  () => W3MChainInfo(
    chainName: 'Sepolia Testnet',
    chainId: '11155111',
    namespace: 'eip155:11155111',
    tokenName: 'SEP',
    rpcUrl: 'https://ethereum-sepolia.publicnode.com',
    blockExplorer: W3MBlockExplorer(
      name: 'Sepolia Etherscan',
      url: 'https://sepolia.etherscan.io/',
    ),
  )
);

3.4 Tương tác với ví và token

Web3Modal_flutter cung cấp cho chúng ta một số widget có sẵn:

Mở ví ra để kết nối:

W3MConnectWalletButton(service: _w3mService) 

Đổi mạng:

W3MNetworkSelectButton(service: _w3mService)

Hiển thị account:

W3MAccountButton(service: _w3mService)

Chuyển token từ tài khoản này sang tài khoản khác:

Future<dynamic> _send({required W3MService w3mService}) async {  
  final deployedContract = DeployedContract(
    ContractAbi.fromJson(
      jsonEncode(SepoliaTestContract.readContractAbi),
      'Tora',
    ),
    EthereumAddress.fromHex(SepoliaTestContract.contractAddress),
  );

  await _w3mService.launchConnectedWallet();
  await w3mService.requestWriteContract(
      topic: w3mService.session?.topic ?? '',
      chainId: 'eip155:11155111',
      rpcUrl: 'https://ethereum-sepolia.publicnode.com',
      deployedContract: deployedContract,
      functionName: 'transfer',
      transaction: Transaction(
        from: EthereumAddress.fromHex(w3mService.session!.address!),
        to: EthereumAddress.fromHex(
            '0x334c8504F136Da92B0...92944cfD1F8023F0523'),  // Replace with address you want to send
        value: EtherAmount.fromInt(EtherUnit.wei, 1),
      ),
  );
}

Chúng ta đã sử dụng readContractAbicontractAddress mà chúng ta đã lưu lại từ bước deploy token.

Sau khi gọi đến function _send này app sẽ gọi đến ví, một pop up hiện ra. Nếu chúng ta chọn Confirm giao dịch sẽ được thực hiện.

Về các chức năng khác của web3modal_flutter các bạn có thể xem tại đây

Ngoài hỗ trợ flutter web3modal cũng hỗ trợ các ngôn ngữ lập trình khác như nextjs, reactjs, vuejs, ….
Các bạn có thể truy cập vào link dưới để tìm hiểu thêm
https://docs.walletconnect.com

4. Kết luận

Vừa rồi tôi đã hướng dẫn qua cho các bạn một cách tổng quan và sơ lược cách hoạt động và cách implement 1 token, deploy, và tạo DApp tương tác với token đó.
Cảm ơn các bạn đã theo dõi, mong rằng nhận được sự tương tác của các bạn.

Viết một bình luận