Preface
In the field of blockchain tradings, decentralized exchanges (DEX) provide a trustless way to exchange assets, and SunSwap, as a representative DEX in the Tron ecosystem, supports smart contract interactions based on Web3. We FMZ platform provides a powerful quantitative trading framework that enables developers to integrate multiple trading strategies.
The FMZ platform already supports the encapsulation of Web3 Ethereum (ETH) exchange objects, and added support for Web3 TRON recently, encapsulating the exchange objects of the TRON chain. Just like accessing UniSwap DEX, access to SunSwap DEX is also possible.
This article will explore how to encapsulate the Web3 interface based on the FMZ Quant platform and access SunSwap DEX for trading. We will introduce the Web3 interaction method of the Tron network, the SunSwap smart contract call, and show how to query liquidity, execute transactions, and obtain transaction results through code examples, so as to build an efficient quantitative trading strategy.
Disclaimer
All codes and designs shared in this article have been tested and are intended for learning and communication. However, even after testing, these contents may still need to be adjusted according to actual needs. If you want to apply them to live trading, please be sure to evaluate, optimize and modify them yourself to ensure that they meet your needs.
Preparation
- Configure the Web3 TRON exchange object of the FMZ platform
In the previous article FMZ Quant Web3 Expansion: Tron Support Added, Expand On-chain Tradings, we have introduced the common methods of Tron gRPC nodes and learned how to configure Web3 TRON exchange objects on FMZ. This article will not repeat these contents. As for the node address, we will use the node provided by TRON official directly.
- Deploy the docker
Unlike CEX exchanges, there is no wall to worry about. You can access the official node by deploying a docker locally.
- SunSwap DEX
Open the front-end page of SunSwap in the browser, you can connect to the wallet and observe the data conveniently.
- tronscan
Essential tools for viewing and analyzing data.
- base58
The address on TRON is base58 encoded, which is different from Ethereum. Sometimes, in order to view and compare data, some conversion is required. On FMZ, you can use the base58 encoded address directly. Some smart contracts return data in HEX encoding, which requires some conversion. Several functions are simply implemented to convert the address format:
The following are the main conversion functions. Due to limited space, I will not post all the codes. The complete code is in the "Tron SunSwap V3 Trading Library" template library disclosed at the end of the article.
functionethAddressToTron(ethAddress) { if (!/^0x[0-9a-fA-F]{40}$/.test(ethAddress)) { throw"Invalid Ethereum address" } const ethAddressBytes = newUint8Array(20) for (let i = 0; i < 20; i++) { ethAddressBytes[i] = parseInt(ethAddress.substr(2 + i * 2, 2), 16) } const tronAddressBytes = newUint8Array(21) tronAddressBytes[0] = 0x41 tronAddressBytes.set(ethAddressBytes, 1) const hash1 = Encode("sha256", "hex", "hex", uint8ArrayToHex(tronAddressBytes)) const hash2 = Encode("sha256", "hex", "hex", hash1) const checksum = hash2.slice(0, 8) const fullAddress = newUint8Array(25) fullAddress.set(tronAddressBytes, 0) fullAddress.set(hexToUint8Array(checksum), 21) returnbase58Encode(fullAddress) }
- Package call
Because sometimes you need to read data from multiple methods at once, calling them one by one is time-consuming and laborious. So a packaged calling function is encapsulated:
functionmulticall(e, multicallContractAddress, data, outTypes) { let ret = e.IO("api", multicallContractAddress, "aggregate", data) if (!ret || !ret["returnData"] || !Array.isArray(ret["returnData"])) { Log("invalid ret:", ret) returnnull } let arrRet = ret["returnData"] if (outTypes.length != arrRet.length) { Log("Arrays have unequal lengths:", arrRet, outTypes) returnnull } let outData = [] for (let i in arrRet) { outData.push(e.IO("decode", outTypes[i], arrRet[i])) } return outData }
Tron SunSwap V3 Trading Library
The design of SunSwap V3 trading library is modeled after the UniSwap trading library template of the FMZ platform. Since the SunSwap template code is long, I will not post it here, and will mainly explain the functions implemented.
Create SunSwap Trading Object
let e = $.CreateSunSwapEx() // By default, the exchange exchanges[0] is used to initialize the SunSwap trading object.// let e = $.CreateSunSwapEx(exchanges[1]) // Initialize the SunSwap trading object using the secondary exchange object.
This template has only one interface function, namely: $.CreateSunSwapEx()
, which is used to create a SunSwap trading object. After the object is created, you can call the method of this object to perform some function calls, such as: exchange transactions, query pool prices, obtain all V3 pool information, pledge, decompression, etc.
GetMarkets
The test codes of "Tron SunSwap V3 Trading Library" are all recorded in the main()
function of the template and will not be repeated here.
let markets = e.GetMarkets() let idx = 0let tbl = {"type": "table", "title": "test GetMarkets", "cols": ["Index", "PoolAddress", "Symbol", "BaseAsset", "QuoteAsset", "BaseName", "QuoteName", "BaseDecimals", "QuoteDecimals", "BaseAddress", "QuoteAddress"], "rows": []} for (let currency in markets) { let arrMarket = markets[currency] for (let market of arrMarket) { tbl["rows"].push([idx, market["PoolAddress"], market["Symbol"], market["BaseAsset"], market["QuoteAsset"], market["BaseName"], market["QuoteName"], market["BaseDecimals"], market["QuoteDecimals"], market["BaseAddress"], market["QuoteAddress"]]) idx++ } } LogStatus("`" + JSON.stringify(tbl) + "`")
The function GetMarkets()
is used to obtain the relevant information of all exchange pools in the SunSwap V3 pool, cached in the variables of the SunSwap trading object, and used to query accuracy, address and other information during other operations.
After running the test code, the information of all V3 pools will be displayed:

The code has removed some of the contract addresses currently seen, such as "phishing contracts" and "invalid contracts".
GetTicker
let ticker1 = e.GetTicker("WTRX_USDT") Log("symbol:", "WTRX_USDT", ", ticker:", ticker1) let ticker2 = e.GetTicker("iCMX_USDT", "TLVDozYEBgeaJXH7oKBsougzEJKNrogFun") // iCMX_USDTLog("symbol:", "iCMX_USDT", ", ticker:", ticker2)
You can use the GetTicker()
function to query the price of a certain exchange pool, and you can specify the address of a specific pool. Note that some pools have poor liquidity and the price is only for reference.
multicall
let data = [] let walletAddress = exchange.IO("address") data.push(["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "0x" + exchange.IO("encode", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "balanceOf", walletAddress)]) data.push(["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "0x" + exchange.IO("encode", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "balanceOf", walletAddress)]) Log(data) let ret = multicall(exchange, "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98", data, ["uint256", "uint256"]) Log(ret, toAmount(ret[0], 6))
This function can be used for subsequent function encapsulation. For example, in the example, two balanceOf
method calls can be encoded and the data can be requested once.
GetSwapOutAmount
let retBuy = e.GetSwapOutAmount("WTRX_USDT", 1, "buy") // 1 quote -> baseLog("WTRX_USDT", "buy outAmount:", retBuy["outAmount"], ", ask:", (1 / retBuy["outAmount"])) let retSell = e.GetSwapOutAmount("WTRX_USDT", 1, "sell") // 1 base -> quoteLog("WTRX_USDT", "sell outAmount:", retSell["outAmount"], ", bid:", (retSell["outAmount"] / 1))
This is an important function, which encapsulates SunSwap's intelligent routing service. Given tokenInSymbol and tokenOutSymbol, it can return possible exchange paths, the number of output tokens and other information.
GetAssets
let assets = e.GetAssets() let tbl = {"type": "table", "title": "test GetAssets", "cols": ["Name", "Address", "Decimals", "Balance"], "rows": []} for (let asset of assets) { tbl["rows"].push([asset.Currency, asset.Address, asset.Decimals, asset.Amount]) } LogStatus("`" + JSON.stringify(tbl) + "`")
Since there is no good mechanism to traverse TRC20 tokens, with the help of tronscan's function, this third-party interface is used to query account asset information.


Swap
// 6 USDT -> ? WTRXlet ret = e.Swap("WTRX_USDT", 6, "buy") Log(ret) // 10 TRX -> ? WTRX// let ret = e.Swap("TRX_WTRX", 10, "sell")// Log(ret)
The most important function is this exchange. When executing the exchange, the exchange path will be formulated according to the smart routing first, and then the exchange method will be called to exchange the token.
It should be noted that if it is a TRX package/unpackage exchange, it is only executed using the WTRX contract method.
We perform the following tests:
let ret = e.Swap("TRX_USDT", 5, "sell") Log(ret)


Other Encapsulations
There are some other functions such as "staking" and "unstaking". You can check them in the main()
function in the template code, so I won't go into details.
You can also expand and add functions such as adding liquidity and removing liquidity.
Template Code
This template does not have any interaction design and interface parameters. It only implements the basic function of accessing SunSwap DEX. More functions can be optimized and added later.
functionisEmptyObject(obj) { returnObject.keys(obj).length === 0 } functioncomputePoolPrice(decimals0, decimals1, sqrtPriceX96) { [decimals0, decimals1, sqrtPriceX96] = [decimals0, decimals1, sqrtPriceX96].map(BigInt) constTWO = BigInt(2) constTEN = BigInt(10) constSIX_TENTH = BigInt(1000000) constQ192 = (TWO ** BigInt(96)) ** TWOreturn (Number((sqrtPriceX96 ** TWO * TEN ** decimals0 * SIX_TENTH) / (Q192 * TEN ** decimals1)) / Number(SIX_TENTH)) } functionmulticall(e, multicallContractAddress, data, outTypes) { let ret = e.IO("api", multicallContractAddress, "aggregate", data) if (!ret || !ret["returnData"] || !Array.isArray(ret["returnData"])) { Log("invalid ret:", ret) returnnull } let arrRet = ret["returnData"] if (outTypes.length != arrRet.length) { Log("Arrays have unequal lengths:", arrRet, outTypes) returnnull } let outData = [] for (let i in arrRet) { outData.push(e.IO("decode", outTypes[i], arrRet[i])) } return outData } functiontoAmount(s, decimals) { returnNumber((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString()) } functiontoInnerAmount(n, decimals) { return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0) } functionhexToUint8Array(hex) { if (hex.length % 2 !== 0) { thrownewError("Invalid hex string length") } returnUint8Array.from( hex.match(/.{1,2}/g).map(byte =>parseInt(byte, 16)) ) } functionuint8ArrayToHex(array) { returnArray.from(array).map(byte => byte.toString(16).padStart(2, "0")).join("") } functionethAddressToTron(ethAddress) { if (!/^0x[0-9a-fA-F]{40}$/.test(ethAddress)) { throw"Invalid Ethereum address" } const ethAddressBytes = newUint8Array(20) for (let i = 0; i < 20; i++) { ethAddressBytes[i] = parseInt(ethAddress.substr(2 + i * 2, 2), 16) } const tronAddressBytes = newUint8Array(21) tronAddressBytes[0] = 0x41 tronAddressBytes.set(ethAddressBytes, 1) const hash1 = Encode("sha256", "hex", "hex", uint8ArrayToHex(tronAddressBytes)) const hash2 = Encode("sha256", "hex", "hex", hash1) const checksum = hash2.slice(0, 8) const fullAddress = newUint8Array(25) fullAddress.set(tronAddressBytes, 0) fullAddress.set(hexToUint8Array(checksum), 21) returnbase58Encode(fullAddress) } functionbase58Encode(buffer) { const base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"let num = BigInt("0x" + Array.from(buffer).map(b => b.toString(16).padStart(2, "0")).join("")) let encoded = ""while (num > 0) { let remainder = num % 58n num = num / 58n encoded = base58Alphabet[Number(remainder)] + encoded } for (let byte of buffer) { if (byte === 0) { encoded = base58Alphabet[0] + encoded } else { break } } return encoded } functionbase58ToHex(base58Str) { constALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"var num = BigInt(0) for (var char of base58Str) { var digit = BigInt(ALPHABET.indexOf(char)) if (digit === BigInt(-1)) thrownewError("Invalid Base58 character: " + char) num = num * BigInt(58) + digit } var hex = num.toString(16) if (hex.length % 2 !== 0) { hex = "0" + hex } return"0x" + hex } $.CreateSunSwapEx = function(e) { let sunSwapEx = {} sunSwapEx.registerABI = function(address, codeABI) { let e = sunSwapEx.eif (typeof(address) == "undefined" || typeof(codeABI) == "undefined") { throw"need address, codeABI" } return e.IO("abi", address, codeABI) } sunSwapEx.getCurrencyInfo = function(token0Address, token1Address) { let e = sunSwapEx.elet arrTokenAddress = [token0Address, token1Address] let tokenInfoCallData = [] let tokenInfoRetType = [] for (let tokenAddress of arrTokenAddress) { tokenInfoCallData.push([tokenAddress, "0x" + e.IO("encode", tokenAddress, "symbol")]) tokenInfoRetType.push("string") tokenInfoCallData.push([tokenAddress, "0x" + e.IO("encode", tokenAddress, "name")]) tokenInfoRetType.push("string") tokenInfoCallData.push([tokenAddress, "0x" + e.IO("encode", tokenAddress, "decimals")]) tokenInfoRetType.push("uint8") } let currencyInfo = _C(multicall, e, multicallContractAddress, tokenInfoCallData, tokenInfoRetType) if (currencyInfo.length != 6) { Log("invalid currency Info:", currencyInfo) returnnull } let ret = { "token0Symbol": currencyInfo[0], "token0Name": currencyInfo[1], "token0Decimals": currencyInfo[2], "token1Symbol": currencyInfo[3], "token1Name": currencyInfo[4], "token1Decimals": currencyInfo[5] } return ret } sunSwapEx.waitMined = function(tx) { let e = sunSwapEx.elet i = 0let maxLoop = 10while (true) { Sleep(3000) let info = e.IO("api", "tron", "GetTransactionInfoByID", tx) if (info && info.receipt && typeof(info.receipt.result) == "number") { Log("GetTransactionInfoByID:", info) if (info.receipt.result == 1) { returntrue } else { returnfalse } } Log("Transaction not yet mined", tx) if (i > maxLoop) { break } i++ } Log(`Transaction: ${tx} not found`) returnfalse } sunSwapEx.getTokenInfo = function(tokenSymbol) { let markets = sunSwapEx.marketsif (!markets || isEmptyObject(markets)) { markets = sunSwapEx.GetMarkets() } if (tokenSymbol == "TRX") { return {"tokenSymbol": tokenSymbol, "tokenName": tokenSymbol, "tokenDecimals": 6, "tokenAddress": "--"} } for (let currency in markets) { for (let market of markets[currency]) { if (market["BaseAsset"] == tokenSymbol) { return {"tokenSymbol": market["BaseAsset"], "tokenName": market["BaseName"], "tokenDecimals": market["BaseDecimals"], "tokenAddress": market["BaseAddress"]} } elseif (market["QuoteAsset"] == tokenSymbol) { return {"tokenSymbol": market["QuoteAsset"], "tokenName": market["QuoteName"], "tokenDecimals": market["QuoteDecimals"], "tokenAddress": market["QuoteAddress"]} } } } Log("not found token symbol:", tokenSymbol) returnnull } sunSwapEx.dealStakeTRX = function(methodName, resourceName, amount) { let e = sunSwapEx.elet walletAddress = e.IO("address") let balance = toInnerAmount(amount, 6) let resourceCode = -1if (resourceName == "BANDWIDTH") { resourceCode = 0 } elseif (resourceName == "ENERGY") { resourceCode = 1 } elseif (resourceName == "TRON_POWER") { resourceCode = 2 } else { Log("not support resourceName:", resourceName) returnnull } return e.IO("api", "tron", methodName, walletAddress, resourceCode, balance) } sunSwapEx.GetMarkets = function() { // sunswap v3 poollet e = sunSwapEx.eif (!sunSwapEx.markets) { sunSwapEx.markets = {} } let markets = sunSwapEx.marketsif (!isEmptyObject(markets)) { return markets } let factoryV3Address = sunSwapEx.factoryV3Address// allPoolsLengthlet poolIdx = e.IO("api", factoryV3Address, "allPoolsLength") if (!poolIdx) { Log("invalid poolIdx:", poolIdx) returnnull } Log("allPoolsLength:", poolIdx) // multicallContractAddresslet multicallContractAddress = sunSwapEx.multicallContractAddress// get All pool addresslet retPools = [] let getPoolsData = [] let retPoolsType = [] for (let i = 0; i < poolIdx; i++) { getPoolsData.push([factoryV3Address, "0x" + e.IO("encode", factoryV3Address, "allPools", String(i))]) retPoolsType.push("address") if (getPoolsData.length > 30 || i == poolIdx - 1) { let arr = _C(multicall, e, multicallContractAddress, getPoolsData, retPoolsType) retPools.push(...arr) getPoolsData = [] retPoolsType = [] } } // allPoolslet poolABI = sunSwapEx.poolABIfor (let i in retPools) { // poolAddresslet poolAddress = ethAddressToTron(retPools[i]) // register pool ABI sunSwapEx.registerABI(poolAddress, poolABI) // get token address of the poollet tokenCallData = [[poolAddress, "0x" + e.IO("encode", poolAddress, "token0")], [poolAddress, "0x" + e.IO("encode", poolAddress, "token1")]] let tokenRetType = ["address", "address"] let arrTokenAddress = _C(multicall, e, multicallContractAddress, tokenCallData, tokenRetType) let token0Address = ethAddressToTron(arrTokenAddress[0]) let token1Address = ethAddressToTron(arrTokenAddress[1]) // symbol , name , decimalslet currencyInfo = sunSwapEx.getCurrencyInfo(token0Address, token1Address) if (!currencyInfo) { returnnull } let token0Symbol = currencyInfo["token0Symbol"] let token0Name = currencyInfo["token0Name"] let token0Decimals = currencyInfo["token0Decimals"] let token1Symbol = currencyInfo["token1Symbol"] let token1Name = currencyInfo["token1Name"] let token1Decimals = currencyInfo["token1Decimals"] // Aliaslet mapAlias = { "TJWm3jWaJeCdyRckEXfopsJBvZi6wXVK2p": "PAPA", "TPYmHEhy5n8TCEfYGqW2rPxsghSfzghPDn": "USDDOLD", "TTiwtPv4MFHMoCpGDBEF85qQjD2v4e99ck": "HOUSDOLD", "TRFe3hT5oYhjSZ6f3ji5FJ7YCfrkWnHRvh": "ETHB" } if (typeof(mapAlias[token0Address]) != "undefined") { token0Symbol = mapAlias[token0Address] } if (typeof(mapAlias[token1Address]) != "undefined") { token1Symbol = mapAlias[token1Address] } // badlet mapBad = { "TMEmRKPob42vhkqEGopCnfBPixN5XTkdUc": "U S D T", // Attention: Fishing contract"TXioUZK8ju3R54KvzSqPc6Ufq5wkxFqpq9": "U S D T", // Attention: Fishing contract"TU41FWQQT8hZ6t72gVEjtSGn1Dshbevi7g": "CNY Coin", // Attention: Invalid"TYB7oXNq4VyuiH5BHb4os8rTh7Ca8pxqSx": "BULL", // Attention: Fishing contract"TYJxozCVMUiHg3TbQp9PKeuXdTsX9ugJz9": "SHISHA", // Attention: Not circulated"TF9io9LGyjuK3uTpr73pAaQ5m9scxd9xvr": "TestToken18", // Attention: Test trading pairs"TK8E3sFhBt3EB6gTT6d6co8RMB6DFUnNwE": "TestToken6"// Attention: Test trading pairs } if (typeof(mapBad[token0Address]) != "undefined" || typeof(mapBad[token1Address]) != "undefined") { continue } // marketlet currency = token0Symbol + "_" + token1Symbol let market = { "Symbol": currency, "BaseAsset": token0Symbol, "QuoteAsset": token1Symbol, "BaseName": token0Name, "QuoteName": token1Name, "BaseDecimals": token0Decimals, "QuoteDecimals": token1Decimals, "BaseAddress": token0Address, "QuoteAddress": token1Address, "PoolAddress": poolAddress, "PoolIndex": i } if (!Array.isArray(markets[currency])) { markets[currency] = [] } markets[currency].push(market) LogStatus(_D(), "get markets, length:", Object.keys(markets).length) Sleep(200) } _G("sunswap_markets", markets) sunSwapEx.markets = markets return markets } sunSwapEx.GetTicker = function(symbol, targetPoolAddress) { let e = sunSwapEx.elet markets = sunSwapEx.marketsif (!markets || isEmptyObject(markets)) { markets = sunSwapEx.GetMarkets() } let arrPoolAddress = [] let arrToken0Decimals = [] let arrToken1Decimals = [] for (let currency in markets) { if (!Array.isArray(markets[currency]) || markets[currency].length == 0) { continue } if (currency == symbol) { for (let ele of markets[currency]) { if (typeof(targetPoolAddress) != "undefined" && ele["PoolAddress"] != targetPoolAddress) { continue } arrPoolAddress.push(ele["PoolAddress"]) arrToken0Decimals.push(ele["BaseDecimals"]) arrToken1Decimals.push(ele["QuoteDecimals"]) } } } if (arrPoolAddress.length == 0) { Log(`${symbol} and ${targetPoolAddress} not found`) sunSwapEx.markets = {} returnnull } let arrPrice = [] let slot0CallData = [] let slot0RetType = [] for (let i in arrPoolAddress) { let poolAddress = arrPoolAddress[i] let poolABI = sunSwapEx.poolABI e.IO("abi", poolAddress, poolABI) slot0CallData.push([poolAddress, "0x" + e.IO("encode", poolAddress, "slot0")]) slot0RetType.push("(uint160,int24,uint16,uint16,uint16,uint8,bool)") } let multicallContractAddress = sunSwapEx.multicallContractAddresslet arrSlot0 = _C(multicall, e, multicallContractAddress, slot0CallData, slot0RetType) for (let i in arrSlot0) { let slot0 = arrSlot0[i] let token0Decimals = arrToken0Decimals[i] let token1Decimals = arrToken1Decimals[i] let sqrtPriceX96 = slot0["Field1"] let poolAddress = arrPoolAddress[i] let price = computePoolPrice(token0Decimals, token1Decimals, sqrtPriceX96) arrPrice.push({"price": price, "poolAddress": poolAddress}) } return arrPrice } sunSwapEx.GetSwapOutAmount = function(symbol, amountIn, type) { let arrCurrency = symbol.split("_") if (arrCurrency.length != 2) { Log("invalid symbol:", symbol) returnnull } let baseCurrencyInfo = sunSwapEx.getTokenInfo(arrCurrency[0]) let quoteCurrencyInfo = sunSwapEx.getTokenInfo(arrCurrency[1]) if (!baseCurrencyInfo || !quoteCurrencyInfo) { returnnull } let baseSymbol = baseCurrencyInfo["tokenSymbol"] let baseAddress = baseCurrencyInfo["tokenAddress"] let baseDecimals = baseCurrencyInfo["tokenDecimals"] let quoteSymbol = quoteCurrencyInfo["tokenSymbol"] let quoteAddress = quoteCurrencyInfo["tokenAddress"] let quoteDecimals = quoteCurrencyInfo["tokenDecimals"] // black hole addressif (baseSymbol == "TRX") { baseAddress = "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb" } if (quoteSymbol == "TRX") { quoteAddress = "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb" } let query = nullif (type == "buy") { let amount = toInnerAmount(amountIn, quoteDecimals) query = `?fromToken=${quoteAddress}&toToken=${baseAddress}&amountIn=${amount}&typeList=WTRX,SUNSWAP_V1,SUNSWAP_V2,SUNSWAP_V3` } elseif (type == "sell") { let amount = toInnerAmount(amountIn, baseDecimals) query = `?fromToken=${baseAddress}&toToken=${quoteAddress}&amountIn=${amount}&typeList=WTRX,SUNSWAP_V1,SUNSWAP_V2,SUNSWAP_V3` } else { Log("not support type:", type) returnnull } try { let ret = JSON.parse(HttpQuery("https://rot.endjgfsv.link/swap/router" + query)) Log("https://rot.endjgfsv.link/swap/router" + query, "GetSwapOutAmount ret:", ret) if (!ret || ret["message"] != "SUCCESS") { Log("invalid data:", ret) returnnull } let outAmount = nulllet best = nulllet info = ret["data"] for (let ele of info) { if (!outAmount) { outAmount = parseFloat(ele["amountOut"]) best = ele } else { let amount = parseFloat(ele["amountOut"]) if (amount > outAmount) { outAmount = amount best = ele } } } if (!outAmount || !best) { Log("info:", info) returnnull } return {"info": info, "outAmount": outAmount, "best": best, "baseCurrencyInfo": baseCurrencyInfo, "quoteCurrencyInfo": quoteCurrencyInfo} } catch(e) { Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message) returnnull } } sunSwapEx.GetAccount = function() { let e = sunSwapEx.elet walletAddress = e.IO("address") let account = e.IO("api", "tron", "GetAccount", walletAddress) if (!account) { Log("get account failed, account:", account) returnnull } let trxBalance = toAmount(account["balance"], 6) let resource = e.IO("api", "tron", "GetAccountResource", walletAddress) if (!resource) { Log("get resource failed, resource:", resource) returnnull } let energyLimit = resource["EnergyLimit"] ? resource["EnergyLimit"] : 0let freeNetLimit = resource["freeNetLimit"] ? resource["freeNetLimit"] : 0return {"Info": {"GetAccount": account, "GetAccountResource": resource}, "TRX": trxBalance, "Bandwidth": freeNetLimit, "Energy": energyLimit} } sunSwapEx.GetAssets = function() { let e = sunSwapEx.elet walletAddress = e.IO("address") try { let ret = JSON.parse(HttpQuery("https://apilist.tronscanapi.com/api/account/token_asset_overview?address=" + walletAddress)) let assets = [] for (let info of ret["data"]) { let decimals = parseInt(info["tokenDecimal"]) let asset = { "Currency": info["tokenAbbr"] == "trx" ? "TRX" : info["tokenAbbr"], "Decimals": decimals, "Amount": toAmount(info["balance"], decimals), "Address": info["tokenId"], } assets.push(asset) } return assets } catch(e) { Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message) returnnull } } sunSwapEx.StakeTRX = function(resourceName, frozenBalance) { return sunSwapEx.dealStakeTRX("FreezeBalanceV2", resourceName, frozenBalance) } sunSwapEx.UnStakeTRX = function(resourceName, unfreezeBalance) { return sunSwapEx.dealStakeTRX("UnfreezeBalanceV2", resourceName, unfreezeBalance) } sunSwapEx.WrappedTRX = function(actionType, amountIn) { let e = sunSwapEx.elet tx = nullif (actionType == "deposit") { let amount = toInnerAmount(amountIn, 6) tx = e.IO("api", "TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR", "deposit", amount, {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)}) } elseif (actionType == "withdraw") { tx = e.IO("api", "TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR", "withdraw(uint256)", toInnerAmount(amountIn, 6), {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)}) } else { Log("not support actionType:", actionType) returnfalse } if (tx) { Log("tx: ", tx) let txRet = sunSwapEx.waitMined(tx) if (!txRet) { Log(actionType, "failed") returnfalse } else { Log(actionType, "success") returntrue } } else { Log("trans error") returnfalse } } sunSwapEx.Swap = function(symbol, amountIn, type) { let e = sunSwapEx.elet smartRouterAddress = sunSwapEx.smartRouterAddressif (symbol == "TRX_WTRX" || symbol == "WTRX_TRX") { let actionType = nullif (type == "buy") { actionType = symbol == "TRX_WTRX" ? "withdraw" : "deposit" } elseif (type == "sell") { actionType = symbol == "TRX_WTRX" ? "deposit" : "withdraw" } else { Log("invalid type:", type) returnfalse } return sunSwapEx.WrappedTRX(actionType, amountIn) } let swapInfo = sunSwapEx.GetSwapOutAmount(symbol, amountIn, type) if (!swapInfo || !swapInfo["best"]) { Log("invalid swapInfo:", swapInfo) returnfalse } let outAmount = swapInfo["outAmount"] let best = swapInfo["best"] // pathlet path = best["tokens"] if (!path || path.length < 2) { Log("invalid path:", path) returnfalse } // poolVersions and versionsLenlet poolVersions = [] let versionsLen = [] for (var v of best["poolVersions"]) { if (poolVersions.length == 0) { poolVersions.push(v) versionsLen.push(2) } else { if (poolVersions[poolVersions.length - 1] == v) { versionsLen[versionsLen.length - 1] += 1 } else { poolVersions.push(v) versionsLen.push(1) } } } // fees let poolFees = best["poolFees"] // get decimals , token name let token0Decimals = swapInfo["baseCurrencyInfo"]["tokenDecimals"] let token1Decimals = swapInfo["quoteCurrencyInfo"]["tokenDecimals"] let tokenInName = type == "buy" ? swapInfo["quoteCurrencyInfo"]["tokenSymbol"] : swapInfo["baseCurrencyInfo"]["tokenSymbol"] let tokenOutName = type == "buy" ? swapInfo["baseCurrencyInfo"]["tokenSymbol"] : swapInfo["quoteCurrencyInfo"]["tokenSymbol"] let tokenInAddress = type == "buy" ? swapInfo["quoteCurrencyInfo"]["tokenAddress"] : swapInfo["baseCurrencyInfo"]["tokenAddress"] let tokenOutAddress = type == "buy" ? swapInfo["baseCurrencyInfo"]["tokenAddress"] : swapInfo["quoteCurrencyInfo"]["tokenAddress"] let tokenInDecimals = type == "buy" ? token1Decimals : token0Decimals // calc amountlet amount = nulllet minAmount = nullif (type == "buy") { amount = toInnerAmount(amountIn, token1Decimals) minAmount = toInnerAmount(outAmount * 0.99, token0Decimals) } elseif (type == "sell") { amount = toInnerAmount(amountIn, token0Decimals) minAmount = toInnerAmount(outAmount * 0.99, token1Decimals) } else { Log("invalid type:", type) returnfalse } // wallet address let walletAddress = e.IO("address") // expired timestamplet expiredTS = parseInt((newDate().getTime() + 1000 * 60 * 5) / 1000) // [amount of the token to be swapped, minimum acceptable amount of the token obtained from the swap, address to receive the token obtained from the swap, deadline]let data = [String(amount), String(minAmount), walletAddress, expiredTS] // allowanceif (tokenInName != "TRX" && !(tokenInName == "WTRX" && tokenOutName == "TRX")) { let allowanceAmount = e.IO("api", tokenInAddress, "allowance", walletAddress, smartRouterAddress) let realAmount = toAmount(allowanceAmount, tokenInDecimals) if (realAmount < amountIn) { // approveLog("realAmount is", realAmount, "too small, try to approve large amount") let txApprove = e.IO("api", tokenInAddress, "approve(address,uint256)", smartRouterAddress, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)}) if (!txApprove) { throw"approve error" } let txRet = sunSwapEx.waitMined(txApprove) if (!txRet) { Log("approve failed") returnfalse } else { Log("approve success") } } else { Log("allowance", realAmount, "no need to approve") } } // swapLog(`path:${path}, poolVersions:${poolVersions}, versionsLen:${versionsLen}, poolFees:${poolFees}, data:${data}`) Log("best swap:", best) let tx = e.IO("api", smartRouterAddress, "swapExactInput(address[],string[],uint256[],uint24[],(uint256,uint256,address,uint256))", tokenInName == "TRX" ? toInnerAmount(amountIn, 6) : 0, path, poolVersions, versionsLen, poolFees, data, {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)}) if (tx) { Log("tx: ", tx) let txRet = sunSwapEx.waitMined(tx) if (!txRet) { Log("swap", tokenInName, "to", tokenOutName, "failed") returnfalse } else { Log("swap", tokenInName, "to", tokenOutName, "success") returntrue } } else { Log("trans error") returnfalse } } sunSwapEx.SendTRX = function(to, amount) { let e = sunSwapEx.ereturn e.IO("api", "tron", "send", to, toInnerAmount(amount, 6)) } sunSwapEx.SetFeeLimit = function(feeLimit) { sunSwapEx.feeLimit = feeLimit Log("SetFeeLimit, feeLimit:", sunSwapEx.feeLimit, ", toInnerAmount:", toInnerAmount(sunSwapEx.feeLimit, 6)) } // initif (typeof(e) == "undefined") { e = exchange Log("By default, the exchange configuration is used") } sunSwapEx.e = e // tronlet ret = e.IO("api", "tron", "GetNodeInfo") if (!ret) { throw"The current Web3 tron ​​exchange object may be misconfigured" } else { Log("node information:", ret) } // feeLimit sunSwapEx.feeLimit = 50// register abilet factoryV3Address = "TThJt8zaJzJMhCEScH7zWKnp5buVZqys9x" sunSwapEx.factoryV3Address = factoryV3Address let poolABI = `[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"indexed":true,"name":"owner","internalType":"address","type":"address"},{"indexed":true,"name":"tickLower","internalType":"int24","type":"int24"},{"indexed":true,"name":"tickUpper","internalType":"int24","type":"int24"},{"indexed":false,"name":"amount","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"amount0","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"amount1","internalType":"uint256","type":"uint256"}],"name":"Burn","anonymous":false,"type":"event"},{"inputs":[{"indexed":true,"name":"owner","internalType":"address","type":"address"},{"indexed":false,"name":"recipient","internalType":"address","type":"address"},{"indexed":true,"name":"tickLower","internalType":"int24","type":"int24"},{"indexed":true,"name":"tickUpper","internalType":"int24","type":"int24"},{"indexed":false,"name":"amount0","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"amount1","internalType":"uint128","type":"uint128"}],"name":"Collect","anonymous":false,"type":"event"},{"inputs":[{"indexed":true,"name":"sender","internalType":"address","type":"address"},{"indexed":true,"name":"recipient","internalType":"address","type":"address"},{"indexed":false,"name":"amount0","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"amount1","internalType":"uint128","type":"uint128"}],"name":"CollectProtocol","anonymous":false,"type":"event"},{"inputs":[{"indexed":true,"name":"sender","internalType":"address","type":"address"},{"indexed":true,"name":"recipient","internalType":"address","type":"address"},{"indexed":false,"name":"amount0","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"amount1","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"paid0","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"paid1","internalType":"uint256","type":"uint256"}],"name":"Flash","anonymous":false,"type":"event"},{"inputs":[{"indexed":false,"name":"observationCardinalityNextOld","internalType":"uint16","type":"uint16"},{"indexed":false,"name":"observationCardinalityNextNew","internalType":"uint16","type":"uint16"}],"name":"IncreaseObservationCardinalityNext","anonymous":false,"type":"event"},{"inputs":[{"indexed":false,"name":"sqrtPriceX96","internalType":"uint160","type":"uint160"},{"indexed":false,"name":"tick","internalType":"int24","type":"int24"}],"name":"Initialize","anonymous":false,"type":"event"},{"inputs":[{"indexed":false,"name":"sender","internalType":"address","type":"address"},{"indexed":true,"name":"owner","internalType":"address","type":"address"},{"indexed":true,"name":"tickLower","internalType":"int24","type":"int24"},{"indexed":true,"name":"tickUpper","internalType":"int24","type":"int24"},{"indexed":false,"name":"amount","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"amount0","internalType":"uint256","type":"uint256"},{"indexed":false,"name":"amount1","internalType":"uint256","type":"uint256"}],"name":"Mint","anonymous":false,"type":"event"},{"inputs":[{"indexed":false,"name":"feeProtocol0Old","internalType":"uint8","type":"uint8"},{"indexed":false,"name":"feeProtocol1Old","internalType":"uint8","type":"uint8"},{"indexed":false,"name":"feeProtocol0New","internalType":"uint8","type":"uint8"},{"indexed":false,"name":"feeProtocol1New","internalType":"uint8","type":"uint8"}],"name":"SetFeeProtocol","anonymous":false,"type":"event"},{"inputs":[{"indexed":true,"name":"sender","internalType":"address","type":"address"},{"indexed":true,"name":"recipient","internalType":"address","type":"address"},{"indexed":false,"name":"amount0","internalType":"int256","type":"int256"},{"indexed":false,"name":"amount1","internalType":"int256","type":"int256"},{"indexed":false,"name":"sqrtPriceX96","internalType":"uint160","type":"uint160"},{"indexed":false,"name":"liquidity","internalType":"uint128","type":"uint128"},{"indexed":false,"name":"tick","internalType":"int24","type":"int24"}],"name":"Swap","anonymous":false,"type":"event"},{"outputs":[{"name":"","internalType":"address","type":"address"}],"inputs":[],"name":"factory","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint24","type":"uint24"}],"inputs":[],"name":"fee","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint256","type":"uint256"}],"inputs":[],"name":"feeGrowthGlobal0X128","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint256","type":"uint256"}],"inputs":[],"name":"feeGrowthGlobal1X128","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint128","type":"uint128"}],"inputs":[],"name":"liquidity","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint128","type":"uint128"}],"inputs":[],"name":"maxLiquidityPerTick","stateMutability":"view","type":"function"},{"outputs":[{"name":"blockTimestamp","internalType":"uint32","type":"uint32"},{"name":"tickCumulative","internalType":"int56","type":"int56"},{"name":"secondsPerLiquidityCumulativeX128","internalType":"uint160","type":"uint160"},{"name":"initialized","internalType":"bool","type":"bool"}],"inputs":[{"name":"","internalType":"uint256","type":"uint256"}],"name":"observations","stateMutability":"view","type":"function"},{"outputs":[{"name":"liquidity","internalType":"uint128","type":"uint128"},{"name":"feeGrowthInside0LastX128","internalType":"uint256","type":"uint256"},{"name":"feeGrowthInside1LastX128","internalType":"uint256","type":"uint256"},{"name":"tokensOwed0","internalType":"uint128","type":"uint128"},{"name":"tokensOwed1","internalType":"uint128","type":"uint128"}],"inputs":[{"name":"","internalType":"bytes32","type":"bytes32"}],"name":"positions","stateMutability":"view","type":"function"},{"outputs":[{"name":"token0","internalType":"uint128","type":"uint128"},{"name":"token1","internalType":"uint128","type":"uint128"}],"inputs":[],"name":"protocolFees","stateMutability":"view","type":"function"},{"outputs":[{"name":"sqrtPriceX96","internalType":"uint160","type":"uint160"},{"name":"tick","internalType":"int24","type":"int24"},{"name":"observationIndex","internalType":"uint16","type":"uint16"},{"name":"observationCardinality","internalType":"uint16","type":"uint16"},{"name":"observationCardinalityNext","internalType":"uint16","type":"uint16"},{"name":"feeProtocol","internalType":"uint8","type":"uint8"},{"name":"unlocked","internalType":"bool","type":"bool"}],"inputs":[],"name":"slot0","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"uint256","type":"uint256"}],"inputs":[{"name":"","internalType":"int16","type":"int16"}],"name":"tickBitmap","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"int24","type":"int24"}],"inputs":[],"name":"tickSpacing","stateMutability":"view","type":"function"},{"outputs":[{"name":"liquidityGross","internalType":"uint128","type":"uint128"},{"name":"liquidityNet","internalType":"int128","type":"int128"},{"name":"feeGrowthOutside0X128","internalType":"uint256","type":"uint256"},{"name":"feeGrowthOutside1X128","internalType":"uint256","type":"uint256"},{"name":"tickCumulativeOutside","internalType":"int56","type":"int56"},{"name":"secondsPerLiquidityOutsideX128","internalType":"uint160","type":"uint160"},{"name":"secondsOutside","internalType":"uint32","type":"uint32"},{"name":"initialized","internalType":"bool","type":"bool"}],"inputs":[{"name":"","internalType":"int24","type":"int24"}],"name":"ticks","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"address","type":"address"}],"inputs":[],"name":"token0","stateMutability":"view","type":"function"},{"outputs":[{"name":"","internalType":"address","type":"address"}],"inputs":[],"name":"token1","stateMutability":"view","type":"function"},{"outputs":[{"name":"tickCumulativeInside","internalType":"int56","type":"int56"},{"name":"secondsPerLiquidityInsideX128","internalType":"uint160","type":"uint160"},{"name":"secondsInside","internalType":"uint32","type":"uint32"}],"inputs":[{"name":"tickLower","internalType":"int24","type":"int24"},{"name":"tickUpper","internalType":"int24","type":"int24"}],"name":"snapshotCumulativesInside","stateMutability":"view","type":"function"},{"outputs":[{"name":"tickCumulatives","internalType":"int56[]","type":"int56[]"},{"name":"secondsPerLiquidityCumulativeX128s","internalType":"uint160[]","type":"uint160[]"}],"inputs":[{"name":"secondsAgos","internalType":"uint32[]","type":"uint32[]"}],"name":"observe","stateMutability":"view","type":"function"},{"outputs":[],"inputs":[{"name":"observationCardinalityNext","internalType":"uint16","type":"uint16"}],"name":"increaseObservationCardinalityNext","stateMutability":"nonpayable","type":"function"},{"outputs":[],"inputs":[{"name":"sqrtPriceX96","internalType":"uint160","type":"uint160"}],"name":"initialize","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"uint256","type":"uint256"},{"name":"amount1","internalType":"uint256","type":"uint256"}],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"tickLower","internalType":"int24","type":"int24"},{"name":"tickUpper","internalType":"int24","type":"int24"},{"name":"amount","internalType":"uint128","type":"uint128"},{"name":"data","internalType":"bytes","type":"bytes"}],"name":"mint","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"uint128","type":"uint128"},{"name":"amount1","internalType":"uint128","type":"uint128"}],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"tickLower","internalType":"int24","type":"int24"},{"name":"tickUpper","internalType":"int24","type":"int24"},{"name":"amount0Requested","internalType":"uint128","type":"uint128"},{"name":"amount1Requested","internalType":"uint128","type":"uint128"}],"name":"collect","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"uint256","type":"uint256"},{"name":"amount1","internalType":"uint256","type":"uint256"}],"inputs":[{"name":"tickLower","internalType":"int24","type":"int24"},{"name":"tickUpper","internalType":"int24","type":"int24"},{"name":"amount","internalType":"uint128","type":"uint128"}],"name":"burn","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"int256","type":"int256"},{"name":"amount1","internalType":"int256","type":"int256"}],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"zeroForOne","internalType":"bool","type":"bool"},{"name":"amountSpecified","internalType":"int256","type":"int256"},{"name":"sqrtPriceLimitX96","internalType":"uint160","type":"uint160"},{"name":"data","internalType":"bytes","type":"bytes"}],"name":"swap","stateMutability":"nonpayable","type":"function"},{"outputs":[],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"amount0","internalType":"uint256","type":"uint256"},{"name":"amount1","internalType":"uint256","type":"uint256"},{"name":"data","internalType":"bytes","type":"bytes"}],"name":"flash","stateMutability":"nonpayable","type":"function"},{"outputs":[],"inputs":[{"name":"feeProtocol0","internalType":"uint8","type":"uint8"},{"name":"feeProtocol1","internalType":"uint8","type":"uint8"}],"name":"setFeeProtocol","stateMutability":"nonpayable","type":"function"},{"outputs":[{"name":"amount0","internalType":"uint128","type":"uint128"},{"name":"amount1","internalType":"uint128","type":"uint128"}],"inputs":[{"name":"recipient","internalType":"address","type":"address"},{"name":"amount0Requested","internalType":"uint128","type":"uint128"},{"name":"amount1Requested","internalType":"uint128","type":"uint128"}],"name":"collectProtocol","stateMutability":"nonpayable","type":"function"}]` sunSwapEx.poolABI = poolABI let multicallContractAddress = "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98" sunSwapEx.multicallContractAddress = multicallContractAddress let multicallContractABI = `[{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct TronMulticall.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBasefee","outputs":[{"internalType":"uint256","name":"basefee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlockNumber","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"chainid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockCoinbase","outputs":[{"internalType":"address","name":"coinbase","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockDifficulty","outputs":[{"internalType":"uint256","name":"difficulty","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getEthBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"},{"internalType":"trcToken","name":"id","type":"trcToken"}],"name":"getTokenBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isContract","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"view","type":"function"}]` sunSwapEx.registerABI(multicallContractAddress, multicallContractABI) let smartRouterAddress = "TCFNp179Lg46D16zKoumd4Poa2WFFdtqYj" sunSwapEx.smartRouterAddress = smartRouterAddress let smartRouterABI = `[{"outputs":[{"name":"amountsOut","type":"uint256[]"}],"inputs":[{"name":"path","type":"address[]"},{"name":"poolVersion","type":"string[]"},{"name":"versionLen","type":"uint256[]"},{"name":"fees","type":"uint24[]"},{"name":"data","type":"tuple"}],"name":"swapExactInput","stateMutability":"payable","type":"function","payable":true}]` sunSwapEx.registerABI(smartRouterAddress, smartRouterABI) // get sunswap_markets sunSwapEx.markets = _G("sunswap_markets") return sunSwapEx } // for testfunctionmain() { // reset logLogReset(1) // reset _G()// _G(null)// Create SunswapEx let e = $.CreateSunSwapEx() // let e = $.CreateSunSwapEx(exchanges[1])// /* GetMarketslet markets = e.GetMarkets() let idx = 0let tbl = {"type": "table", "title": "test GetMarkets", "cols": ["Index", "PoolAddress", "Symbol", "BaseAsset", "QuoteAsset", "BaseName", "QuoteName", "BaseDecimals", "QuoteDecimals", "BaseAddress", "QuoteAddress"], "rows": []} for (let currency in markets) { let arrMarket = markets[currency] for (let market of arrMarket) { tbl["rows"].push([idx, market["PoolAddress"], market["Symbol"], market["BaseAsset"], market["QuoteAsset"], market["BaseName"], market["QuoteName"], market["BaseDecimals"], market["QuoteDecimals"], market["BaseAddress"], market["QuoteAddress"]]) idx++ } } LogStatus("`" + JSON.stringify(tbl) + "`") // *//* GetTicker let ticker1 = e.GetTicker("WTRX_USDT") Log("symbol:", "WTRX_USDT", ", ticker:", ticker1) let ticker2 = e.GetTicker("iCMX_USDT", "TLVDozYEBgeaJXH7oKBsougzEJKNrogFun") // iCMX_USDT Log("symbol:", "iCMX_USDT", ", ticker:", ticker2) *//* multicall let data = [] let walletAddress = exchange.IO("address") data.push(["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "0x" + exchange.IO("encode", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "balanceOf", walletAddress)]) data.push(["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "0x" + exchange.IO("encode", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "balanceOf", walletAddress)]) Log(data) let ret = multicall(exchange, "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98", data, ["uint256", "uint256"]) Log(ret, toAmount(ret[0], 6)) *//* GetSwapOutAmount let retBuy = e.GetSwapOutAmount("WTRX_USDT", 1, "buy") // 1 quote -> base Log("WTRX_USDT", "buy outAmount:", retBuy["outAmount"], ", ask:", (1 / retBuy["outAmount"])) let retSell = e.GetSwapOutAmount("WTRX_USDT", 1, "sell") // 1 base -> quote Log("WTRX_USDT", "sell outAmount:", retSell["outAmount"], ", bid:", (retSell["outAmount"] / 1)) *//* GetAssets let assets = e.GetAssets() let tbl = {"type": "table", "title": "test GetAssets", "cols": ["Name", "Address", "Decimals", "Balance"], "rows": []} for (let asset of assets) { tbl["rows"].push([asset.Currency, asset.Address, asset.Decimals, asset.Amount]) } LogStatus("`" + JSON.stringify(tbl) + "`") *//* Swap // let ret = e.Swap("WTRX_USDT", 6, "buy") // let ret = e.Swap("WTRX_TUSD", 6, "buy") // let ret = e.Swap("WTRX_TUSD", 10, "sell") // let ret = e.Swap("TRX_WTRX", 10, "sell") // let ret = e.Swap("TRX_WTRX", 10, "buy") // let ret = e.Swap("TUSD_TRX", 0.3, "sell") // let ret = e.Swap("WTRX_USDT", 20, "sell") // let ret = e.Swap("USDT_TRX", 6, "sell") Log(ret) *//* check token let mapFilter = {} let checkMarkets = e.GetMarkets() for (let currency in checkMarkets) { for (let market of checkMarkets[currency]) { let base = market["BaseAsset"] let quote = market["QuoteAsset"] if (typeof(mapFilter[base]) == "undefined") { mapFilter[base] = market["BaseAddress"] } if (typeof(mapFilter[quote]) == "undefined") { mapFilter[quote] = market["QuoteAddress"] } if (market["BaseAddress"] != mapFilter[base]) { Log(market["BaseAsset"], market["BaseAddress"], " --- ", base, mapFilter[base]) } if (market["QuoteAddress"] != mapFilter[quote]) { Log(market["QuoteAsset"], market["QuoteAddress"], " --- ", quote, mapFilter[quote]) } } } *//* GetAccount let account = e.GetAccount() Log(account) *//* StakeTRX // BANDWIDTH, ENERGY, TRON_POWER let retStakeTRX = e.StakeTRX("ENERGY", 10) // Stake TRX Log("retStakeTRX:", retStakeTRX) // let retUnStakeTRX = e.UnStakeTRX("ENERGY", 100) // UnStake TRX // Log("retUnStakeTRX:", retUnStakeTRX) *//* send // let ret = e.SendTRX("...", 180) // Log("SendTRX ret:", ret) */ }
END
Through this article, we have realized the Web3 Tron package based on the FMZ Quant platform, and connected to SunSwap DEX successfully, completing the complete process from querying the trading pool to executing the exchange and parsing the transaction results. This not only provides an efficient automation solution for DEX transactions in the Tron ecosystem, but also demonstrates the powerful scalability of the FMZ Quant platform.
I hope that the practical experience in this article can provide valuable reference for your quantitative trading system. Next, I will study the DEX on SOL.
Thank you for reading~
From: Practice of Web3 Tron Encapsulated and Access to SunSwap DEX Based on FMZ Platform