LapTrinhBlockchain

Chia sẻ kiến thức về Lập Trình Blockchain

Lập trình Blockchain, Lập trình Smart Contract

Hướng dẫn triển khai Flashloan / Flashswap sử dụng Sunswap V2, Sunswap V3 trên Nile Testnet của TRON

Hướng dẫn triển khai Flashloan / Flashswap sử dụng Sunswap V2, Sunswap V3 trên Nile Testnet của TRON

Hướng dẫn triển khai Flashloan / Flashswap sử dụng Sunswap V2, Sunswap V3 trên Nile Testnet của TRON

Chia sẻ bài viết
5
(66)

Về cơ bản Sunswap giống với Uniswap, chỉ khác là Sunswap triển khai trên TRON (Nền tảng TVM) còn Uniswap trên khai trên nền tảng EVM. Sunswap V1 giống với Uniswap V1, Sunswap V2 giống với Uniswap V2 và Sunswap V3 giống với Uniswap V3. Vì vậy giống Uniswap, Sunswap hỗ trợ Flashloan / Flashswap:

Có thể bạn quan tâm: Hướng dẫn triển khai Flashloan / Flashswap sử dụng Uniswap V3 trên Goerli TestnetHướng dẫn triển khai Flashloan / Flashswap sử dụng MCL Flashloan, CREAM Finance Flashloan và PancakeSwap trên Binance Smart Chain BSC Testnet

Một số điều kiện cần chuẩn bị trước

Trước khi bắt đầu bạn phải cài đặt TronLink, tạo một account và xin ít TRX trên Nile Testnet qua faucet: Nile Faucet. Ngoài xin TRX, bạn có thể xin token TRC10 và một vài token TRC20.

Bạn nên tìm hiểu sử dụng TronLink, TronScan, cách stake TRX để nhận được Energy và Bandwidth. Nếu bạn chưa biết về Energy và Bandwith, bạn có thể xem thêm: Hiểu về khái niệm Energy và Bandwidth

Bạn cũng nên tìm hiểu về lập trình và triển khai Smart Contract trên TRON network qua bài viết: Hướng dẫn lập trình và triển khai Smart Contract trên TRON network.

Địa chỉ Sunswap trên Nile Testnet

Để có thể thực hiện được Flashloan và Flashswap thì đầu tiên ta cần phải có địa chỉ các contract của Sunswap trên Nile Testnet. Thực tế dự án không public thông tin này, và nếu ta tự triển khai lại contract thì việc tạo pool và thêm thanh khoản cũng mất nhiều thời gian, công sức và cả tài nguyên TRX nữa. Vì thế sử dụng lại địa chỉ có sẵn của dự án là tốt nhất.

May mắn tôi đã tìm được file config.js trên SunSwap3.0-contracts, có thông tin của contract SunswapV3 trên Nile và một số token chính. Thông qua đó tôi tìm tiếp địa chỉ triển khai và tìm ra được contract SmartExchangeRouter, contract này chưa thông tin tất cả các phiên bản V1, V2 và V3:

Địa chỉ một số token chính trên Nile Testnet

Cũng trong tệp config.js, chúng ta có một số địa chỉ các token chính trên Nile Testnet:

Một số pool sử dụng cho Flashswap / Flashloan

Thực tế có rất nhiều pool có thể sử dụng để thực hiện Flashswap / Flashloan:

Tìm hiểu và sử dụng Smart Exchange Router

Tìm hiểu và Smart Exchange Router

Smart Exchange là giao diện tổng hợp thanh khoản của tất cả các dex như SunSwapV1, SunSwapV2, SunSwapV3, Psm, SunCurve để giúp swap giữa các token với độ trượt giá thấp. Việc tính toán path tối ưu thì được tính tập trung thông qua API của Sun.io và khi thực hiện hoán đổi phi tập trung thông qua SmartExchangeRouter. Địa chỉ SmartExchangeRouter trên Nile để bạn tham khảo và test thử: TB6xBCixqRPUSKiXb45ky1GhChFJ7qrfFj

Để hoán đổi token, chúng ta sử dụng hàm swapExactInput():

struct SwapData{
    uint256 amountIn;
    uint256 amountOutMin;
    address to;
    uint256 deadline;
}

function swapExactInput(
    address[] calldata path,
    string[] calldata poolVersion,
    uint256[] calldata versionLen,
    uint24[] calldata fees,
    SwapData calldata data
  ) external nonReentrant payable returns(uint256[] memory amountsOut);

hàm này gồm các tham số:

  • path: Mảng chứa các địa chỉ các token theo thứ tự chuyển đổi. Độ dài mảng path là n thì có n token, tương ứng sẽ có n-1 pool. Riêng token là TRX thì quy ước sử dụng địa chỉ T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb, đây thực ra là địa chỉ null, tức tương ứng với 0x0000000000000000000000000000000000000000 trên hệ Hexa.
  • poolVersion: Mảng để xác định loại pool tương ứng. Mỗi phần tử nhận một trong các giá trị: v1, v2 v3. Và với bất kỳ lệnh chuyển đổi nào giữa TRX WTRX thì poolVersion là v2.
  • versionLen: Xác định số token có trong mỗi loại pool. Tổng các giá trị trong versionLen phải bằng số lượng token trong mảng path. Để khớp như vậy thì giá trị đầu tiên trong versionLen phải cộng thêm 1.
  • fees: Mảng chứa fee của các pool tương ứng, chỉ dùng cho v3, còn lại đều là 0. Yêu cầu độ dài mảng fees phải bằng với độ dài mảng path, do đó để khớp thì phần tử cuối cùng luôn nhận giá trị 0.
  • data: Chứa thông tin thêm bao gồm số lượng, người nhận

Viết Smart Contract sử dụng SmartExchangeRouter

Bây giờ chúng ta sẽ viết Smart Contract sử dụng SmartExchangeRouter ở trên. Code như sau:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0;

// IERC20 interface
interface IERC20 {
  function balanceOf(address owner) external view returns (uint256);
  function approve(address spender, uint256 value) external returns (bool);
}

interface ISmartExchangeRouter {
  struct SwapData {
    uint256 amountIn;
    uint256 amountOutMin;
    address to;
    uint256 deadline;
  }

  function swapExactInput(
    address[] calldata path,
    string[] calldata poolVersion,
    uint256[] calldata versionLen,
    uint24[] calldata fees,
    SwapData calldata data
  ) external payable returns(uint256[] memory amountsOut);
}

contract TestSmartExchangeRouter {
  address constant trxAddr = 0x0000000000000000000000000000000000000000;
  ISmartExchangeRouter public router;
  
  constructor(address _router) {
    router = ISmartExchangeRouter(_router);
  }
  
  function swapToken(address[] calldata path, string[] calldata poolVersion, uint256[] calldata versionLen, uint24[] calldata fees) external {
    address tokenIn = path[0];
    uint256 tokenBalance = 0;
    if (tokenIn==trxAddr) tokenBalance = address(this).balance;
    else tokenBalance = IERC20(tokenIn).balanceOf(address(this));
    ISmartExchangeRouter.SwapData memory swapData = ISmartExchangeRouter.SwapData(tokenBalance, 1, msg.sender, block.timestamp + 1);
    if (tokenIn==trxAddr) {
      router.swapExactInput{value: tokenBalance}(path, poolVersion, versionLen, fees, swapData);
    } else {
      IERC20(tokenIn).approve(address(router), tokenBalance);
      router.swapExactInput(path, poolVersion, versionLen, fees, swapData);
    }
  }
}

Hàm swapToken() sẽ thực hiện convert toàn bộ token hoặc TRX có trong contract, và token đầu ra sẽ được chuyển lại cho người gọi hàm. Vì sử dụng token của contract nên trước khi gọi hàm swapToken(), bạn phải chuyển một ít token vào smart contract trước.

Test thử một số giao dịch

Sử dụng TronIDE để triển khai ta được địa chỉ TH8pAcRH5ebJJMdUMY8WfJ2aQjBe9CbuDz. Bây giờ ta thử 1 số swap:

Swap từ USDT sang TRX trên V1

Để chạy thử case này, đầu tiên chúng ta chuyển 1 USDT vào địa chỉ contract ở trên, sau đó gọi hàm swapToken() với tham số như dưới:

path:        ["TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf", "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb"]
poolVersion: ["v1"]
versionLen:  [2]
fees:        [0, 0]

Chi tiết xem giao dịch:

Swap từ USDT sang TRX trên V2

Đầu tiên chúng ta chuyển 1 lượng USDT vào contract rùi gọi hàm swapToken() với các tham số như dưới. Với trường hợp này ta cần phải có bước chuyển đổi từ WTRX sang TRX:

path:        ["TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf", "TYsbWxNnyTgsZaTFaue9hqpxkU3Fkco94a", "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb"]
poolVersion: ["v1", "v2"]
versionLen:  [2, 1]
fees:        [0, 0, 0]

Chi tiết xem giao dich: ae26e370217e9602a8c7a2a50b1c1bc65e26dc8c36709189b8b9af8100034c5b

Giao dịch swap qua cả V1, V2 và V3

Bây giờ chúng ta sẽ thử swap 10 USDT sang TRX theo thứ tự như sau:

  • Hoán đổi USDT -> WTRX trên V3
  • Hoán đổi WTRX -> JST trên V2
  • Hoán đổi JST -> TRX trên V1

Đầu tiên chúng ta chuyển 10 USDT vào contract rùi gọi hàm swapToken() với các tham số như dưới.

path:        ["TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf", "TYsbWxNnyTgsZaTFaue9hqpxkU3Fkco94a", "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3","T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb"]
poolVersion: ["v3", "v2", "v1"]
versionLen:  [2, 1, 1]
fees:        [100, 0, 0, 0]

Chi tiết xem giao dịch: 5bd3b3c50c958bff817a4ee3f85f22958a97730bca5dddfd74b71d3d3bdafac2

Bài viết này có hữu ích với bạn?

Kích vào một biểu tượng ngôi sao để đánh giá bài viết!

Xếp hạng trung bình 5 / 5. Số phiếu: 66

Bài viết chưa có đánh giá! Hãy là người đầu tiên đánh giá bài viết này.

Trả lời

Giao diện bởi Anders Norén