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 Testnet và Hướ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ục lục
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:
- SunswapV1:
- Factory: TXFouUxm4Qs3c1VxfQtCo4xMxbpwE3aWDM (Từ địa chỉ này bạn có thể lấy được tất cả các pool của V1 và thực hiện các thao tác swap)
- SunswapV2:
- Router: TMn1qrmYUMSTXo9babrJLzepKZoPC7M6Sy (Lấy giá và thực hiện các lệnh swap)
- Factory: THomLGMLhAjMecQf9FQjbZ8a1RtwsZLrGE (Từ địa chỉ này có thể lấy được toàn bộ pool của V2)
- SunswapV3:
- Factory: TUTGcsGDRScK1gsDPMELV2QZxeESWb1Gac (Lấy được toàn bộ pool của V3)
- SwapRouter: TFkswj6rUfK3cQtFGzungCkNXxD2UCpEVD (Thực hiện swap)
- Quoter: TYDW2xFFeoaGkiFMXpFxJz2A6fUHWcPgnK (Lấy thông tin giá)
- StaticQuoter: TWBJYvnjhwVcsApZY8b7Bb8uo4xAxm2P3z
- NFTDescriptor: TT6NrmvQBT7jMJScUvGEeM5cVuaH1UCLNc
- NftPositionDescriptor: TBc5hcm3TjH3hevHjwMnictv5P8s1yF8HT
- NonfungiblePositionManager: TPQzqHbCzQfoVdAV6bLwGDos8Lk2UjXz2R
- SunCurve:
- Pool USDD-USDT: TYDrjNEUywyocckXGNm8Q8JVKVd7fRBcbS
- Pool USDD-USDJ: TXHEVrj9rbtdtEULxPNrTHNn6FrsVSgE15
- Pool USDD-USDJ-3000: TTERruoyVYVNhjzMqakfNCmbww9yziGk5R
- SmartExchange:
- SmartExchangeRouter: TB6xBCixqRPUSKiXb45ky1GhChFJ7qrfFj
- Others:
- Multicall3: TBHG5eViru9SENnYZF5Y3XAAvkohYEVic1 (Hỗ trợ truy vấn dữ liệu trên Blockchain)
Đị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:
- WTRX: TYsbWxNnyTgsZaTFaue9hqpxkU3Fkco94a
- USDJ: TLBaRhANQoJFTqre9Nf1mjuwNWjCJeYqUL
- TUSD: TRz7J6dD2QWxBoumfYt4b3FaiRG23pXfop
- USDC: TWMCMCoJPqCGw5RR7eChF2HoY3a9B8eYA3
- BUSD: TBEzkiB2JUevVNLUnnD8NtCYnnaE9XeviM
- USDD: TGjgvdTWWrybVLaVeFqSyVqJQWjxqRYbaK
- USDT: TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf
- JST: TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3
- SUN: TDqjTkZ63yHB19w2n7vPm2qAkLHwn9fKKk
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:
- SunswapV2-USDT-WTRX: TKioHQsGLkaEWwBwkUB2T4Rm6nwGATtJyh
- …
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 và v3. Và với bất kỳ lệnh chuyển đổi nào giữa TRX và 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:
- cf632f541271bd82b1365ee1ee356864711f2e8ed5c05742ed2f0e2578135031 (1 USDT)
- c2ead0448c68a91dda56e78c84717e753226ff24f54a1d2882a19d58c45baca6 (50 USDT)
- 2f559a3f588651ed74971811187a2ad84116f00353e02e13b95c40cc969697ea (477,168 USDT)
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
Trả lời