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
-
Configurar el objeto de intercambio Web3 TRON de la plataforma FMZ
En el artículo anterior「Expansión cuantitativa de FMZ Web3: se agregó compatibilidad con Tron y se ampliaron las capacidades de transacciones en cadena」En este artículo hemos presentadoTron gRPC Hemos aprendido los métodos comunes de configuración de nodos y cómo configurar el objeto de intercambio Web3 TRON en FMZ. Este artículo no repetirá estos contenidos. En cuanto a la dirección del nodo, utilizaremos directamente el nodo proporcionado oficialmente por TRON.
-
Implementar el host
A diferencia de los intercambios CEX, no hay muros de los que preocuparse. Puede acceder al nodo oficial implementando un host localmente.
-
SunSwap DEX
Abra la página frontal de SunSwap en el navegador, puede conectarse a la billetera y observar los datos cómodamente.
-
tronscan
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.
javascriptfunction 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:
javascriptfunction 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:
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.
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.
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~
- 1




