Type/to search
8
Follow
1364
Followers
FMZプラットフォームに基づくWeb3 TronパッケージングとSunSwap DEXへのアクセスの実践
Discussions
Created 2025-03-21 15:17:05  Updated 2025-03-28 09:52:32
 0
 1112

序文

ブロックチェーン取引の分野では、分散型取引所(DEX)が信頼のない資産交換方法を提供しており、Tronエコシステムの代表的なDEXであるSunSwapは、Web3に基づくスマートコントラクトのやり取りをサポートしています。 FMZ プラットフォーム (FMZ Quantitative) は、開発者が複数の取引戦略を統合できるようにする強力な定量取引フレームワークを提供します。

FMZ プラットフォームはすでに Web3 Ethereum (ETH) 交換オブジェクトのカプセル化をサポートしており、最近では Web3 TRON のサポートが追加され、TRON チェーンの交換オブジェクトがカプセル化されました。 UniSwap DEX へのアクセスと同様に、SunSwap DEX へのアクセスも可能です。

この記事では、FMZ 定量プラットフォームに基づいて Web3 インターフェイスをカプセル化し、トランザクションのために SunSwap DEX に接続する方法について説明します。 Tron ネットワークの Web3 対話方式、SunSwap スマート コントラクト呼び出しを紹介し、コード例を通じて流動性を照会し、トランザクションを実行し、トランザクション結果を取得する方法を示して、効率的な定量的取引戦略を構築します。

声明

この記事で共有されているすべてのコードとデザインはテスト済みであり、学習とコミュニケーションを目的としています。ただし、テスト後でも、実際のニーズに基づいて調整する必要がある場合があります。実際の取引に適用する必要がある場合は、必ずご自身で評価、最適化、修正し、ご自身のニーズを満たしていることを確認してください。

準備を整える

CEX 取引所とは異なり、心配するような壁はありません。ホストをローカルにデプロイすることで、公式ノードにアクセスできます。

  • SunSwap DEX

    https://sun.io/

    ブラウザで SunSwap のフロントエンドページを開くと、ウォレットに接続してデータを簡単に確認できます。

  • tronscan

    https://tronscan.org/

    データを表示および分析するための必須ツール。

  • base58

    TRON 上のアドレスは base58 でエンコードされており、Ethereum とは異なります。データを表示および比較するには、場合によっては変換が必要になります。 FMZ では、base58 でエンコードされたアドレスを直接使用できます。一部のスマート コントラクトは HEX エンコードでデータを返すため、何らかの変換が必要になります。アドレス形式を変換するために、いくつかの関数が簡単に実装されています。

    主な変換機能は以下のとおりです。スペースが限られているため、すべてのコードを掲載することはできません。完全なコードは、記事の最後に公開されている「Tron SunSwap V3 Trading Library」テンプレート ライブラリにあります。

    javascript
    function ethAddressToTron(ethAddress) { if (!/^0x[0-9a-fA-F]{40}$/.test(ethAddress)) { throw "Invalid Ethereum address" } const ethAddressBytes = new Uint8Array(20) for (let i = 0; i < 20; i++) { ethAddressBytes[i] = parseInt(ethAddress.substr(2 + i * 2, 2), 16) } const tronAddressBytes = new Uint8Array(21) tronAddressBytes[0] = 0x41 tronAddressBytes.set(ethAddressBytes, 1) const hash1 = Encode("sha256", "hex", "hex", uint8ArrayToHex(tronAddressBytes)) const hash2 = Encode("sha256", "hex", "hex", hash1) const checksum = hash2.slice(0, 8) const fullAddress = new Uint8Array(25) fullAddress.set(tronAddressBytes, 0) fullAddress.set(hexToUint8Array(checksum), 21) return base58Encode(fullAddress) }
  • パッケージングコール

    一度に複数のメソッドからデータを読み取る必要がある場合があるため、メソッドを 1 つずつ呼び出すのは時間がかかり、面倒です。したがって、パッケージ化された呼び出し関数はカプセル化されます。

    javascript
    function multicall(e, multicallContractAddress, data, outTypes) { let ret = e.IO("api", multicallContractAddress, "aggregate", data) if (!ret || !ret["returnData"] || !Array.isArray(ret["returnData"])) { Log("invalid ret:", ret) return null } let arrRet = ret["returnData"] if (outTypes.length != arrRet.length) { Log("Arrays have unequal lengths:", arrRet, outTypes) return null } let outData = [] for (let i in arrRet) { outData.push(e.IO("decode", outTypes[i], arrRet[i])) } return outData }

Tron SunSwap V3 トレーディングライブラリ

SunSwap V3 取引ライブラリの設計は、FMZ プラットフォームの UniSwap 取引ライブラリ テンプレートをモデルにしています。 SunSwap テンプレートのコードは比較的長いため、ここでは掲載せず、主に実装されている機能について説明します。

SunSwapトランザクションオブジェクトを作成する

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

このテンプレートには、次のインターフェース関数が 1 つだけあります。$.CreateSunSwapEx()SunSwap トランザクション オブジェクトを作成するために使用されます。オブジェクトが作成された後、このオブジェクトのメソッドを呼び出して、償還トランザクション、プール価格の照会、すべての V3 プール情報の取得、誓約、解凍などの機能呼び出しを行うことができます。

GetMarkets

「Tron SunSwap V3 Trading Library」のテストコードはテンプレートにありますmain()この関数は記録されており、ここでは繰り返されません。

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

関数GetMarkets()SunSwap V3 プールのすべての交換プールの関連情報を取得するために使用され、SunSwap トランザクション オブジェクトの変数にキャッシュされ、他の操作中に精度、アドレス、およびその他の情報を照会するために使用されます。

このテスト コードは、実行後にすべての V3 プールに関する情報を表示します。

img

コードにより、「フィッシング契約」や「無効な契約」など、現在見られる契約アドレスの一部が削除されました。

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)

使用可能GetTicker()この関数は、特定の交換プールの価格を照会し、特定のプールのアドレスを指定できます。一部のプールは流動性が非常に低く、価格は参考値のみであることに注意してください。

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

この関数は、後続の関数のカプセル化に使用できます。例えば、例では、balanceOfメソッド呼び出しはエンコードされ、データは 1 回要求されます。

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

これは SunSwap のインテリジェント ルーティング サービスをカプセル化する比較的重要な機能です。 tokenInSymbol と tokenOutSymbol を指定すると、可能な交換パス、出力トークンの数、その他の情報を返すことができます。

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

TRC20 トークンをトラバースするための適切なメカニズムがないため、tronscan のトラバーサルを利用して、このサードパーティ インターフェイスを使用してアカウント資産情報を照会します。

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)

最も重要な機能は交換です。交換を実行すると、インテリジェント ルーティングに従って交換パスが作成され、交換メソッドが呼び出されてトークンが交換されます。
なお、TRX のパッケージ/アンパック交換の場合は、WTRX 契約方式のみで実行されます。

テンプレートコードに追加できる他の関数がいくつかありますmain()機能については、詳細は説明しません。

テンプレートコード

このテンプレートにはインタラクションデザインそしてインタフェースのパラメータSunSwap DEX にアクセスするという基本的な機能のみを実現しており、将来的にはさらに多くの機能が最適化され、追加される可能性があります。

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

この記事を通じて、FMZ定量プラットフォームに基づくWeb3 Tronカプセル化を実現し、SunSwap DEXへの接続に成功し、取引プールのクエリから交換の実行、取引結果の解析までの完全なプロセスを完了しました。これは、Tron エコシステムにおける DEX トランザクションの効率的な自動化ソリューションを提供するだけでなく、FMZ 定量プラットフォームの強力なスケーラビリティも実証します。

この記事の実践的な経験が、あなたの定量取引システムにとって貴重な参考資料となることを願っています。次に、SOL 上の DEX について学習します。
読んでくれてありがとう〜

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