[TOC]
Im Bereich der Blockchain-Transaktionen bieten dezentrale Börsen (DEX) eine vertrauenslose Möglichkeit zum Austausch von Vermögenswerten, und SunSwap unterstützt als repräsentativer DEX im Tron-Ökosystem Smart-Contract-Interaktionen auf Basis von Web3. Die FMZ-Plattform (FMZ Quantitative) bietet ein leistungsstarkes quantitatives Handelsframework, das Entwicklern die Integration mehrerer Handelsstrategien ermöglicht.
Die FMZ-Plattform unterstützt bereits die Kapselung von Web3 Ethereum (ETH)-Austauschobjekten und hat vor Kurzem Unterstützung für Web3 TRON hinzugefügt, wodurch die Austauschobjekte der TRON-Kette gekapselt werden. Genauso wie der Zugriff auf UniSwap DEX ist auch der Zugriff auf SunSwap DEX möglich.
In diesem Artikel wird erläutert, wie die Web3-Schnittstelle basierend auf der quantitativen FMZ-Plattform gekapselt und für Transaktionen eine Verbindung zu SunSwap DEX hergestellt wird. Wir stellen die Web3-Interaktionsmethode des Tron-Netzwerks und den Smart-Contract-Aufruf von SunSwap vor und zeigen anhand von Codebeispielen, wie Liquidität abgefragt, Transaktionen ausgeführt und Transaktionsergebnisse abgerufen werden, um eine effiziente quantitative Handelsstrategie aufzubauen.
Alle in diesem Artikel geteilten Codes und Designs wurden getestet und dienen dem Lernen und der Kommunikation. Allerdings müssen diese auch nach dem Testen möglicherweise noch an den tatsächlichen Bedarf angepasst werden. Wenn Sie es im realen Handel anwenden müssen, achten Sie bitte darauf, es selbst zu bewerten, zu optimieren und zu ändern, um sicherzustellen, dass es Ihren eigenen Anforderungen entspricht.
Im vorherigen Artikel„FMZ Quantitative Web3-Erweiterung: Tron-Unterstützung hinzugefügt, On-Chain-Transaktionsfunktionen erweitert“In diesem Artikel haben wir vorgestelltTron gRPC Wir haben die gängigen Methoden zum Einrichten von Knoten und zum Konfigurieren des Web3-TRON-Austauschobjekts auf FMZ kennengelernt. Dieser Artikel wird diese Inhalte nicht wiederholen. Was die Knotenadresse betrifft, werden wir direkt den offiziell von TRON bereitgestellten Knoten verwenden.
Anders als bei CEX-Börsen gibt es keine Mauern, über die man sich Sorgen machen müsste. Sie können auf den offiziellen Knoten zugreifen, indem Sie lokal einen Host bereitstellen.
Öffnen Sie die Front-End-Seite von SunSwap im Browser, Sie können sich mit der Wallet verbinden und die Daten bequem beobachten.
Wichtige Tools zum Anzeigen und Analysieren von Daten.
Die Adressen auf TRON sind base58-codiert, was sich von Ethereum unterscheidet. Manchmal ist eine Konvertierung erforderlich, um Daten anzuzeigen und zu vergleichen. Auf FMZ können Sie direkt Base58-codierte Adressen verwenden. Einige Smart Contracts geben Daten in HEX-Kodierung zurück, was eine gewisse Konvertierung erfordert. Zur Konvertierung von Adressformaten sind mehrere Funktionen einfach implementiert:
Im Folgenden sind die wichtigsten Konvertierungsfunktionen aufgeführt. Aus Platzgründen werde ich nicht alle Codes veröffentlichen. Der vollständige Code befindet sich in der am Ende des Artikels offengelegten Vorlagenbibliothek „Tron SunSwap V3 Trading Library“.
function ethAddressToTron(ethAddress) {
if (!/^0x[0-9a-fA-F]{40}$/.test(ethAddress)) {
throw "Invalid Ethereum address"
}
const ethAddressBytes = new Uint8Array(20)
for (let i = 0; i < 20; i++) {
ethAddressBytes[i] = parseInt(ethAddress.substr(2 + i * 2, 2), 16)
}
const tronAddressBytes = new Uint8Array(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 = new Uint8Array(25)
fullAddress.set(tronAddressBytes, 0)
fullAddress.set(hexToUint8Array(checksum), 21)
return base58Encode(fullAddress)
}
Da Sie manchmal Daten aus mehreren Methoden gleichzeitig lesen müssen, ist es zeitaufwändig und mühsam, sie einzeln aufzurufen. Daher wird eine gepackte Aufruffunktion gekapselt:
function multicall(e, multicallContractAddress, data, outTypes) {
let ret = e.IO("api", multicallContractAddress, "aggregate", data)
if (!ret || !ret["returnData"] || !Array.isArray(ret["returnData"])) {
Log("invalid ret:", ret)
return null
}
let arrRet = ret["returnData"]
if (outTypes.length != arrRet.length) {
Log("Arrays have unequal lengths:", arrRet, outTypes)
return null
}
let outData = []
for (let i in arrRet) {
outData.push(e.IO("decode", outTypes[i], arrRet[i]))
}
return outData
}
Das Design der SunSwap V3-Handelsbibliothek ist der UniSwap-Handelsbibliotheksvorlage der FMZ-Plattform nachempfunden. Da der SunSwap-Vorlagencode relativ lang ist, werde ich ihn hier nicht veröffentlichen, sondern hauptsächlich die implementierten Funktionen erklären.
let e = $.CreateSunSwapEx() // 默认使用exchange即exchanges[0]初始化SunSwap交易对象。
// let e = $.CreateSunSwapEx(exchanges[1]) // 使用次交易所对象初始化SunSwap交易对象。
Diese Vorlage hat nur eine Schnittstellenfunktion, nämlich:$.CreateSunSwapEx()Wird zum Erstellen von SunSwap-Transaktionsobjekten verwendet. Nachdem das Objekt erstellt wurde, können Sie die Methode dieses Objekts aufrufen, um einige Funktionsaufrufe durchzuführen, z. B.: Rücknahmetransaktionen, Abfragen von Poolpreisen, Abrufen aller V3-Poolinformationen, Verpfändung, Dekomprimierung usw.
Der Testcode der „Tron SunSwap V3 Trading Library“ befindet sich in der Vorlagemain()Die Funktion wurde aufgezeichnet und wird hier nicht wiederholt.
let markets = e.GetMarkets()
let idx = 0
let 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) + "`")
FunktionGetMarkets()Wird verwendet, um relevante Informationen zu allen Austauschpools des SunSwap V3-Pools zu erhalten, in den Variablen der SunSwap-Transaktionsobjekte zwischenzuspeichern und während anderer Vorgänge zum Abfragen von Genauigkeit, Adresse und anderen Informationen zu verwenden.
Dieser Testcode zeigt nach der Ausführung Informationen zu allen V3-Pools an:

Einige der bisher aufgetauchten Vertragsadressen wie „Phishing-Verträge“ und „ungültige Verträge“ wurden aus dem Code entfernt.
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)
VerwendbarGetTicker()Die Funktion fragt den Preis eines bestimmten Exchange-Pools ab und kann die Adresse eines bestimmten Pools angeben. Beachten Sie, dass einige Pools eine sehr geringe Liquidität aufweisen und die Preise nur als Referenz dienen.
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))
Diese Funktion kann zur nachfolgenden Funktionskapselung verwendet werden. Im Beispiel können Sie beispielsweise Folgendes eingeben:balanceOfDer Methodenaufruf erfolgt verschlüsselt und die Daten werden einmalig abgefragt.
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))
Dies ist eine relativ wichtige Funktion, die den intelligenten Routing-Dienst von SunSwap kapselt. Bei gegebenem TokenInSymbol und TokenOutSymbol kann es mögliche Austauschpfade, die Anzahl der Ausgabetoken und andere Informationen zurückgeben.

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) + "`")
Da es keinen guten Mechanismus zum Durchlaufen von TRC20-Token gibt, wird diese Drittanbieterschnittstelle mithilfe der Traversierung von Tronscan zum Abfragen von Kontovermögensinformationen verwendet.


// 6 USDT -> ? WTRX
let ret = e.Swap("WTRX_USDT", 6, "buy")
Log(ret)
// 10 TRX -> ? WTRX
// let ret = e.Swap("TRX_WTRX", 10, "sell")
// Log(ret)
Die wichtigste Funktion ist dieser Austausch. Bei der Ausführung des Austauschs wird der Austauschpfad entsprechend dem intelligenten Routing formuliert und dann die Austauschmethode aufgerufen, um die Token auszutauschen. Bitte beachten Sie, dass ein TRX-Verpackungs-/Entpackungsaustausch nur mit der WTRX-Vertragsmethode ausgeführt wird.
Es gibt einige andere Funktionen, die dem Vorlagencode hinzugefügt werden könnenmain()Auf die Funktion werde ich hier nicht näher eingehen.
Diese Vorlage hat keineInteraktionsdesignUndSchnittstellenparameter, es realisiert nur die Grundfunktion des Zugriffs auf SunSwap DEX, und in Zukunft können weitere Funktionen optimiert und hinzugefügt werden.
”`js function isEmptyObject(obj) { return Object.keys(obj).length === 0 }
function computePoolPrice(decimals0, decimals1, sqrtPriceX96) { [decimals0, decimals1, sqrtPriceX96] = [decimals0, decimals1, sqrtPriceX96].map(BigInt) const TWO = BigInt(2) const TEN = BigInt(10) const SIX_TENTH = BigInt(1000000) const Q192 = (TWO ** BigInt(96)) ** TWO return (Number((sqrtPriceX96 ** TWO * TEN ** decimals0 * SIX_TENTH) / (Q192 * TEN ** decimals1)) / Number(SIX_TENTH)) }
function multicall(e, multicallContractAddress, data, outTypes) { let ret = e.IO(“api”, multicallContractAddress, “aggregate”, data) if (!ret || !ret[“returnData”] || !Array.isArray(ret[“returnData”])) { Log(“invalid ret:”, ret) return null }
let arrRet = ret["returnData"]
if (outTypes.length != arrRet.length) {
Log("Arrays have unequal lengths:", arrRet, outTypes)
return null
}
let outData = []
for (let i in arrRet) {
outData.push(e.IO("decode", outTypes[i], arrRet[i]))
}
return outData
}
function toAmount(s, decimals) { return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString()) }
function toInnerAmount(n, decimals) { return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0) }
function hexToUint8Array(hex) { if (hex.length % 2 !== 0) { throw new Error(“Invalid hex string length”) } return Uint8Array.from( hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)) ) }
function uint8ArrayToHex(array) { return Array.from(array).map(byte => byte.toString(16).padStart(2, “0”)).join(“”) }
function ethAddressToTron(ethAddress) { if (!/^0x[0-9a-fA-F]{40}$/.test(ethAddress)) { throw “Invalid Ethereum address” }
const ethAddressBytes = new Uint8Array(20)
for (let i = 0; i < 20; i++) {
ethAddressBytes[i] = parseInt(ethAddress.substr(2 + i * 2, 2), 16)
}
const tronAddressBytes = new Uint8Array(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 = new Uint8Array(25)
fullAddress.set(tronAddressBytes, 0)
fullAddress.set(hexToUint8Array(checksum), 21)
return base58Encode(fullAddress)
}
function base58Encode(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
}
function base58ToHex(base58Str) { const ALPHABET = “123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz”
var num = BigInt(0)
for (var char of base58Str) {
var digit = BigInt(ALPHABET.indexOf(char))
if (digit === BigInt(-1)) throw new Error("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.e
if (typeof(address) == "undefined" || typeof(codeABI) == "undefined") {
throw "need address, codeABI"
}
return e.IO("abi", address, codeABI)
}
sunSwapEx.getCurrencyInfo = function(token0Address, token1Address) {
let e = sunSwapEx.e
let 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)
return null
}
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.e
let i = 0
let maxLoop = 10
while (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) {
return true
} else {
return false
}
}
Log("Transaction not yet mined", tx)
if (i > maxLoop) {
break
}
i++
}
Log(`Transaction: ${tx} not found`)
return false
}
sunSwapEx.getTokenInfo = function(tokenSymbol) {
let markets = sunSwapEx.markets
if (!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"]}
} else if (market["QuoteAsset"] == tokenSymbol) {
return {"tokenSymbol": market["QuoteAsset"], "tokenName": market["QuoteName"], "tokenDecimals": market["QuoteDecimals"], "tokenAddress": market["QuoteAddress"]}
}
}
}
Log("not found token symbol:", tokenSymbol)
return null
}
sunSwapEx.dealStakeTRX = function(methodName, resourceName, amount) {
let e = sunSwapEx.e
let walletAddress = e.IO("address")
let balance = toInnerAmount(amount, 6)
let resourceCode = -1
if (resourceName == "BANDWIDTH") {
resourceCode = 0
} else if (resourceName == "ENERGY") {
resourceCode = 1
} else if (resourceName == "TRON_POWER") {
resourceCode = 2
} else {
Log("not support resourceName:", resourceName)
return null
}
return e.IO("api", "tron", methodName, walletAddress, resourceCode, balance)
}
sunSwapEx.GetMarkets = function() {
// sunswap v3 pool
let e = sunSwapEx.e
if (!sunSwapEx.markets) {
sunSwapEx.markets = {}
}
let markets = sunSwapEx.markets
if (!isEmptyObject(markets)) {
return markets
}
let factoryV3Address = sunSwapEx.factoryV3Address
// allPoolsLength
let poolIdx = e.IO("api", factoryV3Address, "allPoolsLength")
if (!poolIdx) {
Log("invalid poolIdx:", poolIdx)
return null
}
Log("allPoolsLength:", poolIdx)
// multicallContractAddress
let multicallContractAddress = sunSwapEx.multicallContractAddress
// get All pool address
let 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 = []
}
}
// allPools
let poolABI = sunSwapEx.poolABI
for (let i in retPools) {
// poolAddress
let poolAddress = ethAddressToTron(retPools[i])
// register pool ABI
sunSwapEx.registerABI(poolAddress, poolABI)
// get token address of the pool
let 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 , decimals
let currencyInfo = sunSwapEx.getCurrencyInfo(token0Address, token1Address)
if (!currencyInfo) {
return null
}
let token0Symbol = currencyInfo["token0Symbol"]
let token0Name = currencyInfo["token0Name"]
let token0Decimals = currencyInfo["token0Decimals"]
let token1Symbol = currencyInfo["token1Symbol"]
let token1Name = currencyInfo["token1Name"]
let token1Decimals = currencyInfo["token1Decimals"]
// Alias
let 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]
}
// bad
let mapBad = {
"TMEmRKPob42vhkqEGopCnfBPixN5XTkdUc": "U S D T", // 注意:钓鱼合约
"TXioUZK8ju3R54KvzSqPc6Ufq5wkxFqpq9": "U S D T", // 注意:钓鱼合约
"TU41FWQQT8hZ6t72gVEjtSGn1Dshbevi7g": "CNY Coin", // 注意:无效
"TYB7oXNq4VyuiH5BHb4os8rTh7Ca8pxqSx": "BULL", // 注意:钓鱼合约
"TYJxozCVMUiHg3TbQp9PKeuXdTsX9ugJz9": "SHISHA", // 注意:不流通
"TF9io9LGyjuK3uTpr73pAaQ5m9scxd9xvr": "TestToken18", // 注意:测试交易对
"TK8E3sFhBt3EB6gTT6d6co8RMB6DFUnNwE": "TestToken6" // 注意:测试交易对
}
if (typeof(mapBad[token0Address]) != "undefined" || typeof(mapBad[token1Address]) != "undefined") {
continue
}
// market
let 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.e
let markets = sunSwapEx.markets
if (!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 = {}
return null
}
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.multicallContractAddress
let 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)
return null
}
let baseCurrencyInfo = sunSwapEx.getTokenInfo(arrCurrency[0])
let quoteCurrencyInfo = sunSwapEx.getTokenInfo(arrCurrency[1])
if (!baseCurrencyInfo || !quoteCurrencyInfo) {
return null
}
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 address
if (baseSymbol == "TRX") {
baseAddress = "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb"
}
if (quoteSymbol == "TRX") {
quoteAddress = "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb"
}
let query = null
if (type == "buy") {
let amount = toInnerAmount(amountIn, quoteDecimals)
query = `?fromToken=${quoteAddress}&toToken=${baseAddress}&amountIn=${amount}&typeList=WTRX,SUNSWAP_V1,SUNSWAP_V2,SUNSWAP_V3`
} else if (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)
return null
}
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)
return null
}
let outAmount = null
let best = null
let 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)
return null
}
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)
return null
}
}
sunSwapEx.GetAccount = function() {
let e = sunSwapEx.e
let walletAddress = e.IO("address")
let account = e.IO("api", "tron", "GetAccount", walletAddress)
if (!account) {
Log("get account failed, account:", account)
return null
}
let trxBalance = toAmount(account["balance"], 6)
let resource = e.IO("api", "tron", "GetAccountResource", walletAddress)
if (!resource) {
Log("get resource failed, resource:", resource)
return null
}
let energyLimit = resource["EnergyLimit"] ? resource["EnergyLimit"] : 0
let freeNetLimit = resource["freeNetLimit"] ? resource["freeNetLimit"] : 0
return {"Info": {"GetAccount": account, "GetAccountResource": resource}, "TRX": trxBalance, "Bandwidth": freeNetLimit, "Energy": energyLimit}
}
sunSwapEx.GetAssets = function() {
let e = sunSwapEx.e
let 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)
return null
}
}
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.e
let tx = null
if (actionType == "deposit") {
let amount = toInnerAmount(amountIn, 6)
tx = e.IO("api", "TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR", "deposit", amount, {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)})
} else if (actionType == "withdraw") {
tx = e.IO("api", "TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR", "withdraw(uint256)", toInnerAmount(amountIn, 6), {gasLimit: toInnerAmount(sunSwapEx.feeLimit, 6)})
} else {
Log("not support actionType:", actionType)
return false
}
if (tx) {
Log("tx: ", tx)
let txRet = sunSwapEx.waitMined(tx)
if (!txRet) {
Log(actionType, "failed")
return false
} else {
Log(actionType, "success")
return true
}
} else {
Log("trans error")
return false
}
}
sunSwapEx.Swap = function(symbol, amountIn, type) {
let e = sunSwapEx.e
let smartRouterAddress = sunSwapEx.smartRouterAddress
if (symbol == "TRX_WTRX" || symbol == "WTRX_TRX") {
let actionType = null
if (type == "buy") {
actionType = symbol == "TRX_WTRX" ? "withdraw" : "deposit"
} else if (type == "sell") {
actionType = symbol == "TRX_WTRX" ? "deposit" : "withdraw"
} else {
Log("invalid type:", type)
return false
}
return sunSwapEx.WrappedTRX(actionType, amountIn)
}
let swapInfo = sunSwapEx.GetSwapOutAmount(symbol, amountIn, type)
if (!swapInfo || !swapInfo["best"]) {
Log("invalid swapInfo:", swapInfo)
return false
}
let outAmount = swapInfo["outAmount"]
let best = swapInfo["best"]
// path
let path = best["tokens"]
if (!path || path.length < 2) {
Log("invalid path:", path)
return false
}
// poolVersions and versionsLen
let 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 amount
let amount = null
let minAmount = null
if (type == "buy") {
amount = toInnerAmount(amountIn, token1Decimals)
minAmount = toInnerAmount(outAmount * 0.99, token0Decimals)
} else if (type == "sell") {
amount = toInnerAmount(amountIn, token0Decimals)
minAmount = toInnerAmount(outAmount * 0.99, token1Decimals)
} else {
Log("invalid type:", type)
return false
}
// wallet address
let walletAddress = e.IO("address")
// expired timestamp
let expiredTS = parseInt((new Date().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]
// allowance
if (tokenInName != "TRX" && !(tokenInName == "WTRX" && tokenOutName == "TRX")) {
let allowanceAmount = e.IO("api", tokenInAddress, "allowance", walletAddress, smartRouterAddress)
let realAmount = toAmount(allowanceAmount, tokenInDecimals)
if (realAmount < amountIn) {
// approve
Log("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")
return false
} else {
Log("approve success")
}
} else {
Log("allowance", realAmount, "no need to approve")
}
}
// swap
Log(`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")
return false
} else {
Log("swap", tokenInName, "to", tokenOutName, "success")
return true
}
} else {
Log("trans error")
return false
}
}
sunSwapEx.SendTRX = function(to, amount) {
let e = sunSwapEx.e
return 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))
}
// init
if (typeof(e) == "undefined") {
e = exchange
Log("默认使用exchange配置")
}
sunSwapEx.e = e
// tron
let ret = e.IO("api", "tron", "GetNodeInfo")
if (!ret) {
throw "当前Web3 tron交易所对象可能配置有误"
} else {
Log("node节点信息:", ret)
}
// feeLimit
sunSwapEx.feeLimit = 50
// register abi
let 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[]"}],