Type/to search
8
Follow
1364
Followers
Praxis der Web3-Tron-Verpackung und Zugriff auf SunSwap DEX basierend auf der FMZ-Plattform
Discussions
Created 2025-03-21 15:17:05  Updated 2025-03-28 09:52:32
 0
 1113

Vorwort

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.

Stellungnahme

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.

Vorbereitung

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.

  • SunSwap DEX

    https://sun.io/

    Öffnen Sie die Front-End-Seite von SunSwap im Browser, Sie können sich mit der Wallet verbinden und die Daten bequem beobachten.

  • tronscan

    https://tronscan.org/

    Wichtige Tools zum Anzeigen und Analysieren von Daten.

  • base58

    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“.

    javascript
    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) }
  • Paketaufruf

    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:

    javascript
    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 }

Tron SunSwap V3 Handelsbibliothek

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.

SunSwap-Transaktionsobjekt erstellen

javascript
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.

GetMarkets

Der Testcode der „Tron SunSwap V3 Trading Library“ befindet sich in der Vorlagemain()Die Funktion wurde aufgezeichnet und wird hier nicht wiederholt.

javascript
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:

img

Einige der bisher aufgetauchten Vertragsadressen wie „Phishing-Verträge“ und „ungültige Verträge“ wurden aus dem Code entfernt.

GetTicker

javascript
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.

multicall

javascript
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.

GetSwapOutAmount

javascript
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.

img

GetAssets

javascript
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.

img

img

Swap

javascript
// 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.

Vorlagencode

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.

javascript
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[]"}],"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 test function main() { // reset log LogReset(1) // reset _G() // _G(null) // Create SunswapEx let e = $.CreateSunSwapEx() // let e = $.CreateSunSwapEx(exchanges[1]) // /* GetMarkets 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) + "`") // */ /* 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

In diesem Artikel haben wir die Web3-Tron-Kapselung basierend auf der quantitativen FMZ-Plattform realisiert und erfolgreich eine Verbindung zu SunSwap DEX hergestellt. Dabei haben wir den gesamten Prozess von der Abfrage des Handelspools bis zur Ausführung des Austauschs und der Analyse der Transaktionsergebnisse abgeschlossen. Dies bietet nicht nur eine effiziente Automatisierungslösung für DEX-Transaktionen im Tron-Ökosystem, sondern demonstriert auch die leistungsstarke Skalierbarkeit der quantitativen FMZ-Plattform.

Ich hoffe, dass die praktischen Erfahrungen in diesem Artikel eine wertvolle Referenz für Ihr quantitatives Handelssystem darstellen können. Als nächstes werde ich den DEX auf SOL studieren.
Danke fürs Lesen~

Comment
All comments (0)
No data
No data
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)