Type/to search
8
Follow
1364
Followers
Práctica de empaquetado de Web3 Tron y acceso a SunSwap DEX basado en la plataforma FMZ
Discussions
Created 2025-03-21 15:17:05  Updated 2025-03-28 09:52:32
 0
 1113

Prefacio

En el campo de las transacciones blockchain, los intercambios descentralizados (DEX) proporcionan una forma sin confianza de intercambiar activos, y SunSwap, como DEX representativo en el ecosistema Tron, admite interacciones de contratos inteligentes basadas en Web3. La plataforma FMZ (FMZ Quantitative) proporciona un poderoso marco de negociación cuantitativa que permite a los desarrolladores integrar múltiples estrategias de negociación.

La plataforma FMZ ya admite la encapsulación de objetos de intercambio Web3 Ethereum (ETH), y recientemente agregó soporte para Web3 TRON, encapsulando los objetos de intercambio de la cadena TRON. Al igual que se accede a UniSwap DEX, también se puede lograr acceso a SunSwap DEX.

Este artículo explorará cómo encapsular la interfaz Web3 basada en la plataforma cuantitativa FMZ y conectarse a SunSwap DEX para transacciones. Presentaremos el método de interacción Web3 de la red Tron, la llamada al contrato inteligente SunSwap y mostraremos a través de ejemplos de código cómo consultar liquidez, ejecutar transacciones y obtener resultados de transacciones, para construir una estrategia comercial cuantitativa eficiente.

declaración

Todos los códigos y diseños compartidos en este artículo han sido probados y están destinados al aprendizaje y la comunicación. Sin embargo, incluso después de las pruebas, es posible que aún sea necesario ajustarlos en función de las necesidades reales. Si necesita aplicarlo al trading real, asegúrese de evaluarlo, optimizarlo y modificarlo usted mismo para asegurarse de que satisfaga sus propias necesidades.

Preparaciones

A diferencia de los intercambios CEX, no hay muros de los que preocuparse. Puede acceder al nodo oficial implementando un host localmente.

  • SunSwap DEX

    https://sun.io/

    Abra la página frontal de SunSwap en el navegador, puede conectarse a la billetera y observar los datos cómodamente.

  • tronscan

    https://tronscan.org/

    Herramientas esenciales para visualizar y analizar datos.

  • base58

    Las direcciones en TRON están codificadas en base58, lo cual es diferente de Ethereum. A veces es necesaria alguna conversión para ver y comparar datos. En FMZ, puedes utilizar directamente direcciones codificadas en base58. Algunos contratos inteligentes devuelven datos en codificación HEX, lo que requiere cierta conversión. Se implementan varias funciones de forma sencilla para convertir formatos de direcciones:

    Las siguientes son las principales funciones de conversión. Debido al espacio limitado, no publicaré todos los códigos. El código completo se encuentra en la biblioteca de plantillas "Tron SunSwap V3 Transaction Library", revelada al final del artículo.

    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) }
  • Llamada de paquete

    Dado que a veces es necesario leer datos de varios métodos a la vez, llamarlos uno por uno requiere mucho tiempo y es laborioso. De esta forma, una función de llamada empaquetada queda encapsulada:

    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 }

Biblioteca de trading Tron SunSwap V3

El diseño de la biblioteca comercial SunSwap V3 está inspirado en la plantilla de biblioteca comercial UniSwap de la plataforma FMZ. Dado que el código de la plantilla SunSwap es relativamente largo, no lo publicaré aquí, sino que explicaré principalmente las funciones implementadas.

Crear un objeto de transacción SunSwap

javascript
let e = $.CreateSunSwapEx() // 默认使用exchange即exchanges[0]初始化SunSwap交易对象。 // let e = $.CreateSunSwapEx(exchanges[1]) // 使用次交易所对象初始化SunSwap交易对象。

Esta plantilla solo tiene una función de interfaz, a saber:$.CreateSunSwapEx()Se utiliza para crear objetos de transacción SunSwap. Una vez creado el objeto, puede llamar al método de este objeto para realizar algunas llamadas funcionales, como: transacciones de canje, consultar precios del pool, obtener toda la información del pool V3, compromiso, descompresión, etc.

GetMarkets

El código de prueba de "Tron SunSwap V3 Trading Library" está en la plantillamain()La función ha sido grabada y no se repetirá aquí.

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) + "`")

funciónGetMarkets()Se utiliza para obtener información relevante de todos los grupos de intercambio del grupo SunSwap V3, almacenada en caché en las variables de los objetos de transacción de SunSwap y se utiliza para consultar la precisión, la dirección y otra información durante otras operaciones.

Este código de prueba mostrará información sobre todos los grupos V3 después de ejecutarse:

img

Algunas de las direcciones de contrato como "contratos de phishing" y "contratos no válidos" que se han visto hasta ahora se han eliminado del código.

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)

Se puede usarGetTicker()La función consulta el precio de un determinado pool de intercambio y puede especificar la dirección de un pool específico. Tenga en cuenta que algunos pools tienen muy poca liquidez y los precios son sólo de referencia.

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))

Esta función se puede utilizar para la encapsulación de funciones posteriores. Por ejemplo, en el ejemplo, puedes ponerbalanceOfLa llamada al método se codifica y los datos se solicitan una vez.

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))

Esta es una función relativamente importante, que encapsula el servicio de enrutamiento inteligente de SunSwap. Dados tokenInSymbol y tokenOutSymbol, puede devolver posibles rutas de intercambio, la cantidad de tokens de salida y otra información.

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) + "`")

Dado que no existe un buen mecanismo para atravesar los tokens TRC20, con la ayuda de la travesía de tronscan, se utiliza esta interfaz de terceros para consultar la información de los activos de la cuenta.

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)

La función más importante es el intercambio. Al ejecutar el intercambio, la ruta de intercambio se formulará de acuerdo con el enrutamiento inteligente y luego se llamará al método de intercambio para intercambiar los tokens.
Tenga en cuenta que si se trata de un intercambio de empaquetado/desempaquetado de TRX, solo se ejecuta mediante el método de contrato WTRX.

Hay otras funciones que se pueden agregar al código de la plantillamain()Función, no entraré en detalles.

Código de plantilla

Esta plantilla no tiene ningunaDiseño de interacciónyParámetros de la interfazSolo realiza la función básica de acceder a SunSwap DEX, y se pueden optimizar y agregar más funciones en el futuro.

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

A través de este artículo, hemos realizado la encapsulación Web3 Tron basada en la plataforma cuantitativa FMZ y conectada con éxito a SunSwap DEX, completando el proceso completo desde la consulta al pool comercial hasta la ejecución del intercambio y el análisis de los resultados de la transacción. Esto no solo proporciona una solución de automatización eficiente para las transacciones DEX en el ecosistema Tron, sino que también demuestra la poderosa escalabilidad de la plataforma cuantitativa FMZ.

Espero que la experiencia práctica de este artículo pueda proporcionar una referencia valiosa para su sistema de comercio cuantitativo. A continuación, estudiaré el DEX en SOL.
Gracias por leer~

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