Die Ressourcen sind geladen. Beförderung...

Einfache Einführung in web3-Entwicklung auf Basis von Ethereum mit FMZ

Schriftsteller:Die Erfinder quantifizieren - Kleine Träume, Erstellt: 2023-03-28 13:32:48, Aktualisiert: 2024-11-11 22:28:24

vonBlock: vonBlock, toBlock: zu blockieren, Anschrift : Selbstvertrag Anschrift: Themen: [self.eventHash] - Ich weiß. // Log ((vonBlockNumber:, self.fromBlockNumber, , currBlockNumber:, currBlockNumber, #FF0000)

    var logs = exchange.IO("api", "eth", "eth_getLogs", params)
    if (!logs) {
        return 
    }

    for (var i = 0; i < logs.length; i++) {
        if (toAmount(logs[i].blockNumber, 0) > self.latestBlockNumber) {
            /* TODO: test
            if (self.isFirst) {
                self.firstBlockNumber = toAmount(logs[i].blockNumber, 0)
                Log("firstBlockNumber:", self.firstBlockNumber)
                self.isFirst = false 
            }
            */

            callBack(logs[i])
        }
    }

    self.latestBlockNumber = currBlockNumber
    self.fromBlockNumber = self.latestBlockNumber - 1
}

self.latestBlockNumber = self.getBlockNumber()
self.fromBlockNumber = self.latestBlockNumber - 1

return self

}

VAR-Listener = Null Funktion hauptsächlich Veranstaltung = Überweisung ((Adresse,Adresse,Uint256) Verträge und Vereinbarungen mit Drittländern VAR-Dezimalzeichen =exchange.IO("api", VertragAdresse, Dezimalzeichen) Das ist der Tag.exchange.IO("api", contractAddress, name), "Dezimalzeichen:" Dezimalzeichen)

listener = addEventListener(contractAddress, event, function(log) {        
    var fromAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[1])
    var toAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[2])
    Log("Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0))
    
    /* TODO: test
    arrLog.push(log)
    */
})

while (true) {
    listener.run()
    Sleep(5000)
}

}

/* TODO: Test Var arrLog = [] Die Funktion onexit ((() { Log (Log zum Ende des Laufens, Log zum Verifizieren) var firstBlockNumber = listener.firstBlockNumber var endBlockNumber = listener.latestBlockNumber

Log("getLogs, from:", firstBlockNumber, " -> to:", endBlockNumber)
var fromBlock = "0x" + (firstBlockNumber).toString(16)
var toBlock = "0x" + (endBlockNumber).toString(16)
var params = {
    "fromBlock" : fromBlock,
    "toBlock" : toBlock,
    "topics" : ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],
    "address" : "0xdac17f958d2ee523a2206206994597c13d831ec7"
}
var logs = exchange.IO("api", "eth", "eth_getLogs", params)

Log("arrLog:", arrLog.length)
Log("logs:", logs.length)

if (arrLog.length != logs.length) {
    Log("长度不等!")
    return 
}

for (var i = 0; i < arrLog.length; i++) {
    Log("判断blockNumber:", logs[i].blockNumber == arrLog[i].blockNumber, ",判断from:", logs[i].topics[1] == arrLog[i].topics[1], 
        "判断to:", logs[i].topics[2] == arrLog[i].topics[2])
}

} */


实盘运行:

![img](/upload/asset/16ffd65adc050d33056d.png)

对于执行结果,代码中也编写了验证部分(TODO: test)。经过简单验证可以看到持续监控USDT合约的```Transfer```事件并且记录数据,用这个数据和一次性获取的事件数据对比可以观察出数据一致:

![img](/upload/asset/16e07390a11a606276b1.png)

### 事件过滤

在上一节课程「监听合约事件」的基础上,我们拓展一下,在监听的过程中增加过滤器,监听指定地址的转入转出。当智能合约创建日志时(即释放事件),日志数据中```topics```最多包含4条信息。所以我们设计一个过滤规则,以```[[A1, A2, ...An], null, [C1], D]```为例子。

1、```[A1, A2, ...An]```对应```topics[0]```位置的数据。
2、```null```对应```topics[1]```位置的数据。
3、```[C1]```对应```topics[2]```位置的数据。
4、```D```对应```topics[3]```位置的数据。

- 如果条件结构中的元素设置```null```表示不被过滤,例如```null```对应```topics[1]```,任何值都匹配。
- 如果条件结构中的元素设置单个值表示该位置必须匹配,例如```[C1]```对应```topics[2]```或者```D```对应```topics[3]```,不匹配的日志被过滤。
- 如果条件结构中的元素是一个数组,表示数组中的元素至少有一个要匹配,例如```[A1, A2, ...An]```对应```topics[0]```,```[A1, A2, ...An]```中有任意一个和```topics[0]```匹配,则日志不会被过滤。

**监听交易所的USDT转账**

监控从币安交易所转出、转入币安交易所```USDT```的交易:

```javascript
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 addEventListener(contractAddress, event, callBack) {
    var self = {}
    self.eventHash = "0x" + Encode("keccak256", "string", "hex", event)
    self.contractAddress = contractAddress
    self.latestBlockNumber = 0
    self.fromBlockNumber = 0
    self.firstBlockNumber = 0
    self.filters = []
    
    self.setFilter = function(filterCondition) {
        if (filterCondition.length > 4) {
            throw "filterCondition error"
        }

        self.filters.push(filterCondition)
        Log("设置过滤条件:", filterCondition)
    }

    self.getTokenBalanceOfWallet = function(walletAddress, tokenAddress, tokenDecimals) {
        var balance = exchange.IO("api", tokenAddress, "balanceOf", walletAddress)
        if (balance) {
            return toAmount(balance, tokenDecimals)
        }
        return null
    }

    self.getBlockNumber = function() {
        var maxTry = 10
        for (var i = 0; i < maxTry; i++) {
            var ret = exchange.IO("api", "eth", "eth_blockNumber")
            if (ret) {
                return toAmount(ret, 0)
            }
            Sleep(5000)
        }
        throw "getBlockNumber failed"
    }

    self.run = function() {
        var currBlockNumber = self.getBlockNumber()
        var fromBlock = "0x" + self.fromBlockNumber.toString(16)
        var toBlock = "0x" + currBlockNumber.toString(16)
        var params = {
            "fromBlock" : fromBlock, 
            "toBlock" : toBlock, 
            "address" : self.contractAddress, 
            "topics" : [self.eventHash]
        }
        
        var logs = exchange.IO("api", "eth", "eth_getLogs", params)
        if (!logs) {
            return 
        }

        for (var i = 0; i < logs.length; i++) {
            if (toAmount(logs[i].blockNumber, 0) > self.latestBlockNumber) {
                // 检查过滤条件,设置了过滤条件则执行判断
                if (self.filters.length != 0) {
                    // 初始过滤标记
                    var isFilter = true 
                    // 遍历过滤条件设置
                    for (var j = 0; j < self.filters.length; j++) {
                        // 取一个过滤设置,例如:[[A1, A2, ...An], null, [C1], D]
                        var cond = self.filters[j]

                        // 遍历这个过滤设置
                        var final = true
                        for (var topicsIndex = 0; topicsIndex < cond.length; topicsIndex++) {
                            // 拿到这个过滤设置中的某一个条件,如果是第一个条件:即要和topics[0]对比的数据
                            var condValue = cond[topicsIndex]

                            // 日志中的数据
                            if (topicsIndex > logs[i].topics.length - 1) {
                                continue 
                            }
                            var topicsEleValue = logs[i].topics[topicsIndex]
                            // 如果是Transfer事件,需要处理from和to
                            if (logs[i].topics[0] == "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") {
                                if (topicsIndex == 1 || topicsIndex == 2) {
                                    topicsEleValue = "0x" + exchange.IO("encodePacked", "address", topicsEleValue)
                                }
                            }

                            // 如果condValue类型是数组,表示该位置的对比条件有多个,多个条件对比是逻辑或关系
                            if (Array.isArray(condValue) && condValue.length > 1) {
                                // 判断 condValue[0] == topicsEleValue || condValue[1] == topicsEleValue
                                final = final && condValue.some(element => element === topicsEleValue)
                            }else if (condValue === null) {
                                final = final && true
                            } else {
                                final = final && (condValue === topicsEleValue)
                            }
                        }
                        
                        if (final) {
                            isFilter = false 
                        }
                    }
                    
                    if (isFilter) {
                        continue
                    }
                }
                callBack(logs[i])
            }
        }

        self.latestBlockNumber = currBlockNumber
        self.fromBlockNumber = self.latestBlockNumber - 1
    }

    self.latestBlockNumber = self.getBlockNumber()
    self.fromBlockNumber = self.latestBlockNumber - 1

    return self
}

var listener = null 
function main() {
    // 初始清理日志
    LogReset(1)
    LogProfitReset()

    var event = "Transfer(address,address,uint256)"                          // 监听事件
    var contractAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"       // USDT合约地址
    var decimals = exchange.IO("api", contractAddress, "decimals")           // 获取USDT token的精度信息
    var accountBinanceAddress = "0x28C6c06298d514Db089934071355E5743bf21d60" // Binance 热钱包地址
    accountBinanceAddress = accountBinanceAddress.toLowerCase()              // 地址处理为小写
    Log(exchange.IO("api", contractAddress, "name"), " decimals:", decimals)

    // 创建监听对象
    listener = addEventListener(contractAddress, event, function(log) {
        var fromAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[1])
        var toAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[2])
        if (fromAddress == accountBinanceAddress) {
            Log("币安转出 - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#CD32CD")
        } else if (toAddress == accountBinanceAddress) {
            Log("转入币安 - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#FF0000")
        }        
    })

    // 设置事件过滤
    listener.setFilter([null, accountBinanceAddress, null])   // Binance -> USDT
    listener.setFilter([null, null, accountBinanceAddress])   // USDT -> Binance
    
    var preBalance = 0
    while (true) {
        listener.run()
        var balance = listener.getTokenBalanceOfWallet(accountBinanceAddress, contractAddress, decimals)
        if (balance) {
            var direction = ""
            if (preBalance != 0 && preBalance > balance) {
                direction = " ↓ " + (preBalance - balance) + "#CD32CD"
            } else if (preBalance != 0 && preBalance < balance) {
                direction = " ↑ " + (balance - preBalance) + "#FF0000"
            }
            Log("币安钱包地址:", accountBinanceAddress, " 余额:", balance, direction)
            LogProfit(balance, "&")   // 只画图,不打印日志
            preBalance = balance
        }
        LogStatus(_D(), "币安钱包地址:", accountBinanceAddress, ", 余额:", balance)
        Sleep(5000 * 3)
    }
}

Der Code läuft auf der Festplatte:

img

img

In diesem Kurs zeigen wir Ihnen, wie man einen Ereignisfilter entwirft.USDTTransaktionen. Du kannst dieses Beispiel ändern und erweitern, um alles abzuhören, was dich interessiert.smart moneyIch bin der Meinung, dass es eine gute Idee ist.NFTIch bin der Meinung, dass es eine gute Idee ist, diese Art von Politik zu unterstützen.

Einheitenumwandlung

Viele Berechnungen, die mit Ethereum zu tun haben, haben die Zahlen überschritten.JavaScriptDie maximal sichere ganze Zahl der Sprache. Daher sind einige Methoden zur Bearbeitung großer Zahlen auf der inventor quantified trading platform erforderlich, die wir in früheren Kursen verwendet haben, ohne dass diese in diesem Kapitel ausführlich erläutert werden.

DruckenJavaScriptDie höchste sichere Zahl, die in der Sprache definiert ist:

function main() {
    Log("Number.MAX_SAFE_INTEGER:", Number.MAX_SAFE_INTEGER)
}

Das Ergebnis:

Nummer.MAX_SAFE_INTEGER: 9007199254740991

BigInt

Die kleinste Einheit in Ethereum ist1weiDefinition1GweiDas ist gleich1000000000 wei1GweiIn Ethereum-bezogenen Berechnungen ist es nicht wirklich eine große Zahl, einige Daten sind viel größer als sie.Number.MAX_SAFE_INTEGER: 9007199254740991

Die Erfinder von Quantitative Trading Plattformen nutzen Plattformen, in denen sie ihre Produkte und Dienstleistungen anbieten.BigIntObjekte werden für diese großen ganzen Zahlen verwendet.BigInt()Es ist nicht einfachBigIntObjekte. Sie können Zahlen, sechzehnstellige Zahlenstränge als Parameterstrukturen verwenden.BigIntObjekte ─ VerwendungBigIntGegenstandtoString()Die Methode liefert die Daten, die durch die Objekte dargestellt werden, in Stringform.

BigIntObjektunterstützte Operationen:

  • Das ist die Art, wie man das macht.+
  • Abzüglich:-
  • Die Multiplikationsoperation:*
  • Es geht um:/
  • Die Formeln:%
  • Das ist ein sehr schwieriges Problem.**

Hier ein Beispiel für den Code:

function main() {
    // 1Gwei的十进制表示
    var oneGwei = 1000000000

    // 1Gwei的十进制转换为十六进制表示
    var oneGweiForHex = "0x" + oneGwei.toString(16)

    Log("oneGwei : ", oneGwei)
    Log("oneGweiForHex : ", oneGweiForHex)

    // 构造BigInt对象
    Log("1Gwei / 1Gwei : ", (BigInt(oneGwei) / BigInt(oneGweiForHex)).toString(10))
    Log("1Gwei * 1Gwei : ", (BigInt(oneGwei) * BigInt(oneGweiForHex)).toString(10))
    Log("1Gwei - 1Gwei : ", (BigInt(oneGwei) - BigInt(oneGweiForHex)).toString(10))
    Log("1Gwei + 1Gwei : ", (BigInt(oneGwei) + BigInt(oneGweiForHex)).toString(10))
    Log("(1Gwei + 1) % 1Gwei : ", (BigInt(oneGwei + 1) % BigInt(oneGweiForHex)).toString(10))
    Log("1Gwei ** 2 : ", (BigInt(oneGwei) ** BigInt(2)).toString(10))
    Log("100 的平方根 : ", (BigInt(100) ** BigFloat(0.5)).toString(10))

    Log("Number.MAX_SAFE_INTEGER : ", BigInt(Number.MAX_SAFE_INTEGER).toString(10))
    Log("Number.MAX_SAFE_INTEGER * 2 : ", (BigInt(Number.MAX_SAFE_INTEGER) * BigInt("2")).toString(10))
}

Tests mit Debugging-Tools:

2023-06-08 11:39:50		信息	Number.MAX_SAFE_INTEGER * 2 : 18014398509481982
2023-06-08 11:39:50		信息	Number.MAX_SAFE_INTEGER : 9007199254740991
2023-06-08 11:39:50		信息	100 的平方根 : 10
2023-06-08 11:39:50		信息	1Gwei ** 2 : 1000000000000000000
2023-06-08 11:39:50		信息	(1Gwei + 1) % 1Gwei : 1
2023-06-08 11:39:50		信息	1Gwei + 1Gwei : 2000000000
2023-06-08 11:39:50		信息	1Gwei - 1Gwei : 0
2023-06-08 11:39:50		信息	1Gwei * 1Gwei : 1000000000000000000
2023-06-08 11:39:50		信息	1Gwei / 1Gwei : 1
2023-06-08 11:39:50		信息	oneGweiForHex : 0x3b9aca00
2023-06-08 11:39:50		信息	oneGwei : 1000000000

BigFloat

BigFloatObjekte undBigIntObjektverwendungen sind ähnlich, sie werden für größere Zahlen verwendet und unterstützen die Multiplikationsoperation.BigFloatObjektunterstützungtoFixed()Wie geht das?

Hier ein Beispiel für den Code:

function main() {
    var pi = 3.14
    var oneGwei = "1000000000"
    var oneGweiForHex = "0x3b9aca00"

    Log("pi + oneGwei : ", (BigFloat(pi) + BigFloat(oneGwei)).toFixed(2))
    Log("pi - oneGweiForHex : ", (BigFloat(pi) - BigFloat(oneGweiForHex)).toFixed(2))
    Log("pi * 2.0 : ", (BigFloat(pi) * BigFloat(2.0)).toFixed(2))
    Log("pi / 2.0 : ", (BigFloat(pi) / BigFloat(2.0)).toFixed(2))
}

Tests mit Debugging-Tools:

2023-06-08 13:56:44		信息	pi / 2.0 : 1.57
2023-06-08 13:56:44		信息	pi * 2.0 : 6.28
2023-06-08 13:56:44		信息	pi - oneGweiForHex : -999999996.86
2023-06-08 13:56:44		信息	pi + oneGwei : 1000000003.14

Großes Zehntelzeichen

BigDecimalObjekte sind mit Integern, Schwellpunkten und anderen Funktionen kompatibel.BigIntSie haben sich nicht verändert.BigFloatObjektinitialierung, unterstützt auch die Multiplikationsoperation.

Hier ein Beispiel für den Code:

function main() {
    var pi = 3.1415
    var oneGwei = 1000000000
    var oneGweiForHex = "0x3b9aca00"

    Log("pi : ", BigDecimal(pi).toFixed(2))
    Log("oneGwei : ", BigDecimal(oneGwei).toString())
    Log("oneGweiForHex : ", BigDecimal(BigInt(oneGweiForHex)).toString())

    Log("BigInt(oneGwei) : ", BigDecimal(BigInt(oneGwei)).toString())    
    Log("BigFloat(pi) : ", BigDecimal(BigFloat(pi)).toFixed(4))

    Log("oneGwei + pi : ", (BigDecimal(oneGwei) + BigDecimal(pi)).toString())
    Log("oneGwei - pi : ", (BigDecimal(oneGwei) - BigDecimal(pi)).toString())
    Log("2.0 * pi : ", (BigDecimal(2.0) * BigDecimal(pi)).toString())
    Log("pi / pi : ", (BigDecimal(pi) / BigDecimal(pi)).toString())
}

Das Debugger-Tool läuft auf:

2023-06-08 14:52:53		信息	pi / pi : 1
2023-06-08 14:52:53		信息	2.0 * pi : 6.283
2023-06-08 14:52:53		信息	oneGwei - pi : 999999996.8585
2023-06-08 14:52:53		信息	oneGwei + pi : 1000000003.1415
2023-06-08 14:52:53		信息	BigFloat(pi) : 3.1415
2023-06-08 14:52:53		信息	BigInt(oneGwei) : 1e+9
2023-06-08 14:52:53		信息	oneGweiForHex : 1e+9
2023-06-08 14:52:53		信息	oneGwei : 1e+9
2023-06-08 14:52:53		信息	pi : 3.14

Einheitenumwandlung

Die beiden folgenden Funktionen:toAmount()toInnerAmount()Diese beiden Funktionen, die wir in früheren Kursen oft verwendet haben, werden hauptsächlich für die Datenpräzisionsumwandlung verwendet.

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

toAmount()Die Funktion wird eine VariablesSie werden in der Lage sein,decimalsVerwenden Sie eine Konvertierung. In der tatsächlichen Entwicklung von Web3 ist es oft notwendig, sechzehnstellige Daten an einigen Ketten zu verarbeiten. Wir haben oft in unseren früheren Kursen gesehen, wie zum Beispiel bei Smart Contracts.Transfer(address,address,uint256)In den EreignissendataFelddaten:

{
	"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000",
	"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000006b75d8af000000e20b7a7ddf000ba900b4009a80", "0x000000000000000000000000bcb095c1f9c3dc02e834976706c87dee5d0f1fb6"],
	"transactionHash": "0x27f9bf5abe3148169b4b85a83e1de32bd50eb81ecc52e5af006157d93353e4c4",
	"transactionIndex": "0x0",
	"removed": false,
	"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
	"blockHash": "0x847be24a7b159c292bda030a011dfec89487b70e71eed486969b032d6ef04bad",
	"blockNumber": "0x109b1cc",
	"logIndex": "0x0"
}

Datenverarbeitung"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000"Ich habe es nicht verstanden.toAmount()Funktionen. Ein solches Verarbeitungsdesign ist sehr gut.dataDie Felddaten werden in lesbare Werte umgewandelt.

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}

function main() {
    var data = "0x00000000000000000000000000000000000000000000000001c1a55000000000"
    Log(toAmount(data, 18))  // 打印出 0.12656402755905127
}

Ein ETH-Token ist, wie wir wissen,1e18 weiWenn wir eineweiDaten in Einheiten126564027559051260Wie kann man die Anzahl der ETH-Token umwandeln? NutzungtoAmount(, 18)Die Funktion kann einfach umgewandelt werden.toInnerAmount()Die Funktion ist:toAmount()Die Funktion kann umgekehrt (nach Präzision, Vergrößerung) ausgeführt werden, sodass die beiden Funktionen bequem verwendet werden können, um Daten zu konvertieren.

Es ist wichtig zu beachten, dass die integrierte Sicherheitsspanne in der JavaScript-SpracheNumber.MAX_SAFE_INTEGERDas folgende Beispiel zeigt ein Problem, das bei der Datenumwandlung eher verborgen ist:

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 main() {
    var amount = 0.01
    var innerAmount = Number(toInnerAmount(amount, 18))       

    Log("Number.MAX_SAFE_INTEGER:", Number.MAX_SAFE_INTEGER)  // 9007199254740991
    Log("innerAmount:", innerAmount)                          // 10000000000000000

    Log("typeof(innerAmount):", typeof(innerAmount), ", innerAmount:", innerAmount)
    
    // 十进制数值 10000000000000000 -> 十六进制数值 0x2386f26fc10000
    Log("转换", innerAmount, "为十六进制:", innerAmount.toString(16))
    Log("转换", BigInt(10000000000000000).toString(10), "为十六进制:", BigInt(10000000000000000).toString(16))
    
    Log("0x" + BigInt(10000000000000000).toString(16), "转换为10进制:", toAmount("0x" + BigInt(10000000000000000).toString(16), 0))
}

Die Debugger-Tools können unter folgendem Namen ausgeführt werden:

2023-06-15 16:21:40		信息	0x2386f26fc10000 转换为10进制: 10000000000000000
2023-06-15 16:21:40		信息	转换 10000000000000000 为十六进制: 2386f26fc10000
2023-06-15 16:21:40		信息	转换 10000000000000000 为十六进制: 10000000000000000
2023-06-15 16:21:40		信息	typeof(innerAmount): number , innerAmount: 10000000000000000
2023-06-15 16:21:40		信息	innerAmount: 10000000000000000
2023-06-15 16:21:40		信息	Number.MAX_SAFE_INTEGER: 9007199254740991

Wir haben beobachtet, was wir herausgefunden haben:

Log("转换", innerAmount, "为十六进制:", innerAmount.toString(16))

Die entsprechende Log-Ausgabe der Codezeile lautet:转换 10000000000000000 为十六进制: 10000000000000000Das ist natürlich, weil 100000000000000000000 überschritten wurde.Number.MAX_SAFE_INTEGER

Aber wenn ein Dezimalwert im Sicherheitsbereich ist, dann ist er kleiner alsNumber.MAX_SAFE_INTEGERIch bin nicht derjenige.toString(16)Funktionsumwandlung ist normal, zum Beispiel:

function main() {
    var value = 1000
    Log("把value转换为十六进制:", "0x" + value.toString(16))   // 0x3e8
    Log("把0x3e8转换为十进制:", Number("0x3e8"))               // 1000
}

Es ist nicht einfach, sich in der Blockchain-Welt zu bewegen.0.01Ein ETH wird fürweiEinheitliche Zahlenwerte10000000000000000Sie werden auch weiter gehen.Number.MAX_SAFE_INTEGEREs ist also sicherer, wenn man sich in einer solchen Situation umwandelt:BigInt(10000000000000000).toString(16)

Analoger Anruf

Es gibt eine Reihe von Methoden, wie man auf Ethereum Transaktionen durchführt, Smart Contracts aufruft.WriteMethoden müssen eine gewisse Gaskostenverbrauch erfordern und sind manchmal mit dem Risiko des Scheiterns verbunden. Es ist sehr wichtig zu wissen, welche Transaktionen scheitern können, bevor man Transaktionen sendet oder aufruft.

Eth_call

RPC-Methode für Ethereumeth_call: kann eine Transaktion simulieren und mögliche Transaktionsergebnisse zurückgeben, die aber nicht wirklich auf der Blockchain ausgeführt werden.

eth_callDie Methode hat zwei Parameter, der erste ist eine Lexikonstruktur, der zweite ist eine Lexikonstruktur.transactionObject

// transactionObject
{
    "from" : ...,     // The address from which the transaction is sent
    "to" : ...,       // The address to which the transaction is addressed
    "gas" : ...,      // The integer of gas provided for the transaction execution
    "gasPrice" : ..., // The integer of gasPrice used for each paid gas encoded as hexadecimal
    "value" : ...,    // The integer of value sent with this transaction encoded as hexadecimal
    "data" : ...,     // The hash of the method signature and encoded parameters. For more information, see the Contract ABI description in the Solidity documentation
}

Der zweite Parameter lautetblockNumberSie können tags übertragen:latest/pending/earliestUnd so weiter:

/* blockNumber
The block number in hexadecimal format or the string latest, earliest, pending, safe or 
finalized (safe and finalized tags are only supported on Ethereum, Gnosis, Arbitrum, 
Arbitrum Nova and Avalanche C-chain), see the default block parameter description in 
the official Ethereum documentation
*/

Das nächste, was wir tun, sind Token.DAIDie Smart Contract MethodeapprovetransferEin Beispiel für einen Analog-Aufruf ist die folgende Testumgebung für das Ethereum Netz.

Analoger Anruf genehmigen

Die Erweiterung der ERC20-VerträgeapproveDie Methode ist für uns alle bekannt und wurde in früheren Kursen praktiziert. Da ERC20-Kontrakte bereits in der FMZ-Plattform ABI integriert sind, müssen wir nicht die ABI registrieren, die wir anrufen möchten.

function main() {
    var contractAddressUniswapV3SwapRouterV2 = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"
    var contractAddress_DAI = "0x6b175474e89094c44da98b954eedeac495271d0f"
    var wallet = exchange.IO("address")

    // encode approve
    var data = exchange.IO("encode", contractAddress_DAI, "approve(address,uint256)", 
        contractAddressUniswapV3SwapRouterV2, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
    Log("ERC20 token DAI approve encode, data:", data)
    
    var transactionObject = {
        "from" : wallet,
        "to" : contractAddress_DAI,
        // "gasPrice" : "0x" + parseInt("21270894680").toString(16),
        // "gas" : "0x" + parseInt("21000").toString(16),
        "data" : "0x" + data,
    }
    var blockNumber = "latest"
    
    var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
    Log("ret:", ret)
}

Der Code in diesem Beispiel wird zuerstapprove(address,uint256)Die Methoden, die Parameter, die Codes,approveDie Parameterwerte der Methode0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffZeigt die maximale Anzahl von Autorisierungen an. Autorisiert Smart Contracts, Adressen0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45Das heißtUniswap V3Der Routing-Vertrag.eth_callDie Simulationen werden hier gezeigt.transactionObjectIn den ParameterngasPricegasSie können diese Felder überspringen.

Debugger-Tool läuft, simulierter Anruf der approve-Methode autorisiert erfolgreich (und nicht wirklich autorisiert):

2023-06-09 11:58:39		信息	ret: 0x0000000000000000000000000000000000000000000000000000000000000001
2023-06-09 11:58:39		信息	ERC20 token DAI approve encode, data: 095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

Wir können auch Szenarien simulieren, in denen wir versagen, wenn wir anpassen.gasPriceundgasWenn die ETH in der Brieftasche nicht ausreicht, um Gas zu bezahlen, gibt es einen Fehler:

nicht ausreichende Mittel

Wenn Gaspreise zu niedrig eingestellt werden, gibt es Fehler:

Eigengas zu niedrig: haben 21000, wollen 21944 (geliefertes Gas 21000)

Analoger Aufruf von Transfer

Für ERC20transferDie Methode, mit der wir ERC20-Token an eine Wallet-Adresse überweisen können, ist uns auch nicht unbekannt.

function toInnerAmount(n, decimals) {
    return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}

function main() {
    var walletVitalik = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
    var contractAddress_DAI = "0x6b175474e89094c44da98b954eedeac495271d0f"
    var wallet = exchange.IO("address")

    // 转账给V神
    var decimals_DAI = exchange.IO("api", contractAddress_DAI, "decimals")
    var transferAmount = toInnerAmount(1000, decimals_DAI)
    Log("转账金额:", 1000, "DAI, 使用 toInnerAmount 转换为:", transferAmount)

    // encode transfer
    var data = exchange.IO("encode", contractAddress_DAI, "transfer(address,uint256)",
        walletVitalik, transferAmount)

    var transactionObject = {
        "from" : wallet,
        "to" : contractAddress_DAI,
        "data" : "0x" + data,
    }
    var blockNumber = "latest"
    
    var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
    return ret 
}

Da mein Test-Wallett keine DAI-Token hat, gab es bei der Ausführung des Debugging-Tools eine Fehlermeldung:

Ausführung rückgängig gemacht: Dai/unzureichendes Guthaben

Sieh dir diese Geldbörse an:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045Wir haben die DAI-Token in diesem Portemonnaie. Also lassen Sie uns die Überweisungsrichtung der Analogie anpassen.

Ich habe den Code geändert, und dort, wo ich ihn geändert habe, habe ich eine Bemerkung gemacht:

function toInnerAmount(n, decimals) {
    return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}

function main() {
    var walletVitalik = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
    var contractAddress_DAI = "0x6b175474e89094c44da98b954eedeac495271d0f"
    var wallet = exchange.IO("address")

    var decimals_DAI = exchange.IO("api", contractAddress_DAI, "decimals")
    var transferAmount = toInnerAmount(1000, decimals_DAI)
    Log("转账金额:", 1000, "DAI, 使用 toInnerAmount 转换为:", transferAmount)

    // encode transfer
    var data = exchange.IO("encode", contractAddress_DAI, "transfer(address,uint256)",
        wallet, transferAmount)     // 使用wallet变量作为参数,转账接收方地址改为我自己

    var transactionObject = {
        "from" : walletVitalik,     // 使用walletVitalik变量作为from字段的值,模拟这个调用是由V神钱包地址发出
        "to" : contractAddress_DAI,
        "data" : "0x" + data,
    }
    var blockNumber = "latest"
    
    var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
    Log(ret)
}

Tests mit Debugging-Tools:

2023-06-09 13:34:31		信息	0x0000000000000000000000000000000000000000000000000000000000000001
2023-06-09 13:34:31		信息	转账金额: 1000 DAI, 使用 toInnerAmount 转换为: 1000000000000000000000

Die Ergebnisse von Transaktionen können mit der Quantifizierungsplattform der Erfinder sehr einfach simuliert werden, um zu vermeiden, dass die Versendung von möglicherweise fehlgeschlagenen Transaktionen zu unnötigen Gaskosten führt.eth_callEs gibt noch mehr Möglichkeiten.eth_callWo ist die Methode?

Identifizierung von ERC721-Verträgen

Wir wissen, dass Token wie ETH und BTC zu den homogenen Token gehören, und das, was Sie in Ihrer Hand haben, ist nicht anders als das, was ich in meiner Hand habe. Aber es gibt viele Dinge in der Welt, die nicht homogener sind, z. B. Immobilien, Antiquitäten, virtuelle Kunstwerke, die nicht mit homogenen Token abstrahiert werden können. Daher gibt es den ERC721-Standard, um nicht-homogene Objekte zu abstrahieren. Wie erkennen wir also, welche Smart Contracts unter den vielen Smart Contracts, die auf Ethereum bereitgestellt werden, Smart Contracts nach dem ERC721 Standard sind?

Um ERC721 zu erkennen, muss man sich zunächst mit dem ERC165-Standard auskennen.

ERC165

Durch den ERC165-Standard kann ein Smart Contract die von ihm unterstützten Interfaces für die Prüfung anderer Verträge deklarieren. Der ERC165-Interface-Vertrag hat nur eine Funktion:supportsInterface(bytes4 interfaceId), ParameterinterfaceIdDie Interface-Id, die gerade abgefragt wird. Wenn der Vertrag die Interface-Id implementiert hat, kehrt der Bull-Wert einen wahren Wert zurück, andernfalls einen falschen Wert.

Wir werden darüber sprechen.interfaceIdWie genau werden sie berechnet und codiert?

ERC165-StandardEin Beispiel:

pragma solidity ^0.4.20;

interface Solidity101 {
    function hello() external pure;
    function world(int) external pure;
}

contract Selector {
    function calculateSelector() public pure returns (bytes4) {
        Solidity101 i;
        return i.hello.selector ^ i.world.selector;
    }
}

Für die Funktionssignierung der Schnittstelle (die aus einer Liste von Funktionsnamen und Parametertypen besteht) wird eine Abweichung oder Berechnung vorgenommen. Für einen ERC165-Schnittstellenvertrag mit nur einer Funktion:

pragma solidity ^0.4.20;

interface ERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

Die Schnittstellenkennzeichnung für diese Schnittstelle ist 0x01ffc9a7. Sie können dies berechnen, indem Sie bytes4 ((keccak256 ((supportsInterface(bytes4) )); oder den oben genannten Selector-Vertrag verwenden.

Wenn wir die Funktionssignatur direkt berechnen und die ersten vier Bytes nehmen, dann ergeben wirinterfaceId

function main() {
    var ret = Encode("keccak256", "string", "hex", "supportsInterface(bytes4)")
    Log("supportsInterface(bytes4) interfaceId:", "0x" + ret.slice(0, 8))
}

In Debugging Tools können folgende Tests durchgeführt werden:

2023-06-13 14:53:35		信息	supportsInterface(bytes4) interfaceId: 0x01ffc9a7

Sie können sehen, dass die Ergebnisse berechnet undERC165-StandardDie Beschreibung ist in den Dokumenten übereinstimmend.

Erstattung

Als nächstes betrachten wir die Interface-Definition des ERC721-Vertragsstandards:

interface ERC721 /* is ERC165 */ {
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    function balanceOf(address _owner) external view returns (uint256);

    function ownerOf(uint256 _tokenId) external view returns (address);

    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    function approve(address _approved, uint256 _tokenId) external payable;

    function setApprovalForAll(address _operator, bool _approved) external;

    function getApproved(uint256 _tokenId) external view returns (address);

    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

Wenn wir entscheiden wollen, ob ein Smart Contract ein ERC721-Vertrag ist, müssen wir zunächst wissen, was ein ERC721-Vertrag bedeutet.interfaceIdDann können Sie versuchen, es zu benutzen.supportsInterface(bytes4 interfaceId)Methoden, in den vorherigen Kursen haben wir einige Konzepte und Berechnungen des ERC165-Standards kennengelernt.interfaceIdWir schreiben die Algorithmen direkt in den Code:

function calcSelector(arrSelector) {
    var ret = null
    if (Array.isArray(arrSelector)) {
        if (arrSelector.length == 1) {
            ret = Encode("keccak256", "string", "hex", arrSelector[0])
        } else if (arrSelector.length == 0) {
            throw "错误:数组中元素个数为0"
        } else {
            var viewEncodeData = null
            for (var i = 0; i < arrSelector.length; i++) {
                if (i == 0) {
                    ret = new Uint8Array(Encode("keccak256", "string", "raw", arrSelector[i]))
                } else {
                    viewData = new Uint8Array(Encode("keccak256", "string", "raw", arrSelector[i]))
                    
                    if (viewData.length != ret.length) {
                        throw "错误:TypeArray view长度不同"
                    }

                    for (var index = 0; index < ret.length; index++) {
                        ret[index] ^= viewData[index]
                    }
                }
            }
            ret = Encode("raw", "raw", "hex", ret.buffer)
        }
    } else {
        throw "错误:参数需要数组类型。"
    }

    return "0x" + ret.slice(0, 8)
}

function main() {
    // supportsInterface(bytes4): 0x01ffc9a7
    // var ret = calcSelector(["supportsInterface(bytes4)"])

    // ERC721Metadata: 0x5b5e139f
    /* 
    var arrSelector = [
        "name()",
        "symbol()",
        "tokenURI(uint256)"
    ]
    var ret = calcSelector(arrSelector)
    */

    // ERC721: 0x80ac58cd
    // /*
    var arrSelector = [
        "balanceOf(address)",
        "ownerOf(uint256)",
        "safeTransferFrom(address,address,uint256,bytes)",
        "safeTransferFrom(address,address,uint256)",
        "transferFrom(address,address,uint256)",
        "approve(address,uint256)",
        "setApprovalForAll(address,bool)",
        "getApproved(uint256)",
        "isApprovedForAll(address,address)",
    ]
    var ret = calcSelector(arrSelector)
    // */

    Log(ret)
}

Wir haben es in unserem Code benutzt.Encode()Funktion für die Funktionssignaturberechnungkeccak256Für die Berechnung in den oben genannten CodebeispielenEncode()Die Ausgabe der Funktion ist"raw"Die Funktion wird zurückgegebenJavaScriptSpracheArrayBufferDer Typ. Wenn man zweiArrayBufferObjekt durchgeführt^(Differenz-) Operationen, basierend aufArrayBufferObjektentwicklungTypedArrayDie Daten werden dann einzeln ausgewertet, um die Daten zu durchsuchen.

Das Debugger-Tool läuft auf:

2023-06-13 15:04:09		信息	0x80ac58cd

Sie können sehen, wie die Ergebnisse berechnet werden.eip-721Die Beschreibung ist übereinstimmend.

pragma solidity ^0.4.20;

/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x80ac58cd.
interface ERC721 /* is ERC165 */ {
    /// @dev This emits when ownership of any NFT changes by any mechanism.
    ///  This event emits when NFTs are created (`from` == 0) and destroyed
    ///  (`to` == 0). Exception: during contract creation, any number of NFTs
    ///  may be created and assigned without emitting Transfer. At the time of
    ///  any transfer, the approved address for that NFT (if any) is reset to none.
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
...

Mit der Interface-ID von ERC721 können wir entscheiden, ob ein Vertrag nach dem ERC721-Standard ist.BAYCUm das zu testen, ist es ein Vertrag, der sich an ERC721 orientiert, und zuerst müssen wir ABI registrieren, da wir nur die folgenden drei Methoden aufrufen:

  • Unterstützt Interface (InterfaceId)
  • Symbol (n)
  • Name (n)

Die spezifischen Codes sind:

function main() {
    // ERC721的合约地址,这里用的BAYC
    var testContractAddress = "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"

    var testABI = `[{
        "inputs": [{
            "internalType": "bytes4",
            "name": "interfaceId",
            "type": "bytes4"
        }],
        "name": "supportsInterface",
        "outputs": [{
            "internalType": "bool",
            "name": "",
            "type": "bool"
        }],
        "stateMutability": "view",
        "type": "function"
    }, {
        "inputs": [],
        "name": "symbol",
        "outputs": [{
            "internalType": "string",
            "name": "",
            "type": "string"
        }],
        "stateMutability": "view",
        "type": "function"
    }, {
        "inputs": [],
        "name": "name",
        "outputs": [{
            "internalType": "string",
            "name": "",
            "type": "string"
        }],
        "stateMutability": "view",
        "type": "function"
    }]`

    // ERC721接口Id,在之前的课程中计算得出
    var interfaceId = "0x80ac58cd"

    // 注册ABI
    exchange.IO("abi", testContractAddress, testABI)

    // 调用supportsInterface方法
    var isErc721 = exchange.IO("api", testContractAddress, "supportsInterface", interfaceId)

    // 输出信息
    Log("合约地址:", testContractAddress)
    Log("合约名称:", exchange.IO("api", testContractAddress, "name"))
    Log("合约代号:", exchange.IO("api", testContractAddress, "symbol"))
    Log("合约是否为ERC721标准:", isErc721)
}

In Debugging Tools können folgende Tests durchgeführt werden:

2023-06-13 16:32:57		信息	合约是否为ERC721标准: true
2023-06-13 16:32:57		信息	合约代号: BAYC
2023-06-13 16:32:57		信息	合约名称: BoredApeYachtClub
2023-06-13 16:32:57		信息	合约地址: 0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d

Adresse ermitteln0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13dDer Vertrag ist der ERC721-Standard.

In diesem Beitrag haben wir erklärt, wie man einen ERC721-Vertrag beurteilt, wenn ein ERC20-Vertrag, der den ERC165-Standard nicht unterstützt, auf andere Weise identifiziert werden kann.

Code für Calldata

Was ist das?calldataDie einfache, gebräuchliche Beschreibung lautet, wie der Autor es versteht:

"Calldata" ist die Codierung eines Funktionsanrufs oder Parameters in Ethereum, die nach den ABI-Spezifikationen des Vertrags codiert wird.

Wir können zum Beispiel die ERC20-Kontrakte, die wir in den vorherigen Kursen gelernt haben, mit der ERC20-Kontrakte verknüpfen.balanceOftransferDie Methodenanrufe werden zusammen mit den Parametern beim Aufruf als einecalldataIn einigen Anwendungsszenarien, zum Beispiel:Interaktionen zwischen den VerträgenIch bin der Meinung, dass es in diesem Fall sinnvoll ist.calldataEs gibt natürlich noch viele andere Anwendungsfälle, die ich hier auflisten werde.

Wie kann man einen Smart Contract-Funktionsanruf programmieren, umcalldata

Es gibt viele Möglichkeiten, wie Sie Ihre Produkte auf der Inventor Quantitative Trading Plattform verwenden können.exchange.IO("encode", ...)Die Anrufe von Smart Contract-Funktionen sind sehr einfach zu bedienen und zu kodieren.exchange.IO("encode", ...)Der erste Parameter der Funktion ist eine feste String."encode"; der zweite Parameter ist die Adresse des Smart Contracts; der dritte Parameter ist der Name der Smart Contract Methode, die kodiert werden soll; die restlichen Parameter werden in den spezifischen Parameterwert der Smart Contract Methode, die kodiert werden soll, übertragen.

Eth_sendRawTransaction wird verwendet

Wenn wir einen Anruf für eine Smart-Contract-Methode programmieren und eine entsprechendecalldataWenn die Smart-Contract-Methode eine Write-Methode ist (d.h. eine Schreiboperation), dann müssen wir die erzeugten Daten anpassen.calldataDaten als Datenfelder für Transaktionen und dann mit Ethereum's RPC Methodeeth_sendRawTransactionEine Anfrage mit den ursprünglichen Daten der Transaktion wird an das Ethereum-Netzwerk gesendet.

eth_sendRawTransactionDie Methode hat nur einen Parameter.data

Daten: Die unterzeichnete Transaktion (normalerweise mit einer Bibliothek unter Verwendung Ihres privaten Schlüssels)

Das hier.dataEin Parameter ist ein Signaturrechner für Transaktionsdaten. Die Transaktionsdatenstruktur von Ethereum besteht aus folgenden Bereichen:

{
    "nonce": "0x1",                         // 交易发送方的账户交易次数
    "gasPrice": "0x12a05f200",              // 交易的Gas价格
    "gasLimit": "0x5208",                   // 交易的Gas限制
    "to": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2",    // 目标合约地址或接收方地址
    "value": "0x4563918244F40000",          // 转账的以太币数量
    "data": "0x0123456789ABCDEF",           // 要发送给合约的数据
}

Wie unterschreibt man sich für einen Ethereum-Austausch?

Wir benutzen die Quantitative Trading Plattform der Erfinder.Encode()Eine Funktion, die Signaturen berechnet, wie wir es in unserem folgenden Kurs "Write method calldata" geschrieben haben.

Ausführen der Read-Methode calldata

Für die Read MethodecalldataWir haben die RPC-Methode verwendet, die wir zuvor gelernt haben:eth_callWir haben es vorhin erklärt.eth_callDiese Methode der RPC von Ethereum macht nur Smart Contracts.WriteEine Demonstration der Methode, die in diesem Kapitel verwendet wirdcalldataDie Methode, wie man einen Smart Contract-Read-Methode aufruft.balanceOfWie man den WETH-Token-Balance der aktuellen Brieftasche liest.

Wir haben ein Debugger-Tool verwendet, um das Ethereum-Netz zu testen:

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}

function main() {
    // WETH合约的ABI
    var abiWETH = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}]`

    // WETH合约地址
    var wethAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"

    // 注册WETH合约的ABI
    exchange.IO("abi", wethAddress, abiWETH)

    // 当前配置的交易所对象的钱包地址
    var walletAddress = exchange.IO("address")

    // 编码WETH合约的deposit方法调用
    var calldataForDeposit = exchange.IO("encode", wethAddress, "balanceOf(address)", walletAddress)
    Log("calldataForDeposit:", "0x" + calldataForDeposit)

    // 构造transaction,作为eth_call的第一个参数
    var transaction = {
        "from" : walletAddress,
        "to" : wethAddress,
        "data" : "0x" + calldataForDeposit,
    }

    // eth_call的第二个参数
    var blockNumber = "latest"

    // 使用eth_call调用
    var ret = exchange.IO("api", "eth", "eth_call", transaction, blockNumber)
    var wethBalance = exchange.IO("decode", "uint256", ret)   // 可以使用exchange.IO("decode", ...) 函数解码
    Log("wethBalance:", toAmount(wethBalance, 18))            // 从以wei为单位,换算成WETH个数为单位
}

Das Debugger-Tool läuft auf:

2023-06-15 11:51:31		信息	wethBalance: 0.015
2023-06-15 11:51:31		信息	calldataForDeposit: 0x70a082310000000000000000000000006b3f11d807809b0b1e5e3243df04a280d9f94bf4

Wenn die Methode des Smart Contracts einen Return-Value hat, kann sie verwendet werden.exchange.IO("decode", ...)Die Funktionsentzifferung.calldataWie kann man einen Smart Contract direkt aufrufen?balanceOfDie Methode ist die gleiche, ich habe einen WETH-Balance von 0,015 WETH in meinem Test-Portemonnaie.

Ausführen der Write-Methode calldata

Für die Ausführung der Calldata der Write-Methode wird die RPC-Methode verwendet:eth_sendRawTransactionDas ist alles.

Wir haben ein Debugger-Tool verwendet, um das Ethereum-Netz zu testen:

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}

function toInnerAmount(s, decimals) {
    return (BigDecimal(s)*BigDecimal(Math.pow(10, decimals))).toFixed(0)
}

function main() {
    // WETH合约的ABI
    var abiWETH = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed

Mehr