Die Ressourcen sind geladen. Beförderung...

Beginnen Sie mit der Entwicklung von Web3 auf Basis von Ethereum mit FMZ

Schriftsteller:FMZ~Lydia, Erstellt: 2023-06-25 09:17:53, aktualisiert: 2024-11-11 22:34:49

0000000000164f2434262e1cc", Transaktion Hash: 0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048, Adresse : 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2, blockHash: 0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9 - Ich weiß. Adresse : 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2, blockHash: 0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9, Blocknummer: 0x109b1cd, logIndex: 0xde, entfernt: falsch, Themen: [0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65, 0x00000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b], Daten: 0x0000000000000000000000000000000000000000000000000000000164f2434262e1cc, Transaktion Hash: 0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048, Transaktionsindex: 0x91 - Ich weiß.


We can see that there are various events in the logs data, if we only care about ```Transfer``` events, we need to filter out the ```Transfer``` events in these data.

### Retrieving Logs

The Ethereum log is divided into two parts: 1. ```topics```; 2. ```data```.

- ```topics```
  Taking the results of the code run for the ```eth_getLogs``` section test as an example, the data in the ```topics``` field is:

  ```desc
  "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c", "0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],  

Der Wert dertopicsFeld ist eine Arraystruktur, die zur Beschreibung des Ereignisses verwendet wird. Es ist festgelegt, dass seine (Array-) Länge 4 nicht überschreiten darf und das erste Element der Signatur-Hash des Ereignisses ist. In der FMZ Quant Trading Plattform können wir diesen Signatur-Hash mit derEncodeFunktion mit dem folgenden Code:

function main() {
    var eventFunction = "Transfer(address,address,uint256)"
    var eventHash = Encode("keccak256", "string", "hex", eventFunction)
    Log("eventHash:", "0x" + eventHash)
    // eventHash: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
}

Berechnen Sie diekeccak256Hash-Wert (Hex-Codierung) vonTransfer(address,address,uint256)ist0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef.

Der Wert dertopicsFeld ist eine Arraystruktur mit dem zweiten Element und dem dritten Element:

  • Versandadressefrom

  • Empfangsadresseto

  • data

    Die Daten indataFeld sind:

    "data": "0x0000000000000000000000000000000000000000000000000164f2434262e1cc",
    

    Bestimmte Parameter im Falle (Parameter ohne indexierte Erklärung im Soliditätscode des Smart Contracts) werden in derdata section.

    Analyse der Daten0x0000000000000000000000000000000000000000000000000164f2434262e1cc

    function toAmount(s, decimals) {
        return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
    }
    
    function main() {
        var value = "0x0000000000000000000000000000000000000000000000000164f2434262e1cc"
        Log(toAmount(value, 0) / 1e18)  // 0.10047146239950075
    }
    

    Diese Daten werden als 0,10047146239950075 und diedataist der entsprechende Transferbetrag.


Wir haben alles erklärt, geübt und sind bereit, die Protokolle abzurufen.

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() {
    // getBlockNumber
    var blockNumber = exchange.IO("api", "eth", "eth_blockNumber")
    Log("blockNumber:", blockNumber)

    // get logs
    var fromBlock = "0x" + (toAmount(blockNumber, 0) - 1).toString(16)
    var toBlock = "0x" + toAmount(blockNumber, 0).toString(16)
    var params = {
        "fromBlock" : fromBlock,
        "toBlock" : toBlock,
        "address" : "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
    }
    var logs = exchange.IO("api", "eth", "eth_getLogs", params)

    // Traverse logs
    var eventFunction = "Transfer(address,address,uint256)"
    var eventHash = "0x" + Encode("keccak256", "string", "hex", eventFunction)
    Log("eventHash:", eventHash)

    var counter = 0
    for (var i = logs.length - 1; i >= 0 && counter < 10; i--) {
        if (logs[i].topics[0] == eventHash) {
            Log("Event Transfer, data:", toAmount(logs[i].data, 0) / 1e18, ", blockNumber:", toAmount(logs[i].blockNumber, 0), ", transactionHash:", logs[i].transactionHash,
              ", log:", logs[i])
            counter++
        }
    }
}

Überprüfen Sie.https://etherscan.io/:

img

Ergebnisse des im FMZ-Debugging-Tool ausgeführten Testcodes:

img

Daten in derfrom, toFeld können auch je nach Bedarf zum Zeitpunkt des Abrufs analysiert werden, z. B.:

function main() {
    var from = "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c"
    var address = "0x" + exchange.IO("encodePacked", "address", from)
    Log("address:", address)
}

Laufende Ergebnisse:

Adresse: 0x12b791bb27b3a4ee958b5a435fea7d49ec076e9c

Hören Sie auf Vertragsabschlüsse

Seit demDebugging-ToolIn diesem Abschnitt verwenden wir die FMZ Quant Trading Plattform, um Live-Trading zu erstellen, um zu testen.

Hier verwenden wir das Ethereum Mainnet, und wir hören dieTransfer(address,address,uint256)Veranstaltung desUSDTBasierend auf dem, was wir in der letzten Lektion gelernt haben, haben wir ein Beispiel entworfen und geschrieben, wie wir kontinuierlich auf die Ereignisse eines bestimmten intelligenten Vertrages hören:

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
    /* TODO: test
    self.isFirst = true 
    */ 

    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]
        }
        // Log("fromBlockNumber:", 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 
function main() {
    var event = "Transfer(address,address,uint256)"
    var contractAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"
    var decimals = exchange.IO("api", contractAddress, "decimals")
    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])
        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 = []
function onexit() {
    Log("End the run and verify the record")
    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("Length varies!")
        return 
    }
    
    for (var i = 0; i < arrLog.length; i++) {
        Log("Determine the blockNumber:", logs[i].blockNumber == arrLog[i].blockNumber, ", Determine from:", logs[i].topics[1] == arrLog[i].topics[1], 
            "Determine to:", logs[i].topics[2] == arrLog[i].topics[2])
    }
}
*/

Auf Live-Trading läuft:

img

Für die Ausführungsergebnisse wird auch ein Validierungsabschnitt (TODO: test) in den Code geschrieben.TransferDie Daten über den USDT-Kontrakt werden kontinuierlich überwacht und erfasst, und ein Vergleich zwischen diesen Daten und den sofort erfassten Daten über den USDT-Kontrakt kann feststellen, dass die Daten folgendermaßen übereinstimmen:

img

Filterung von Ereignissen

Basierend auf der vorherigen LektionListening to contract events, erweitern wir es durch Hinzufügen von Filtern zum Abhörprozess, um auf Transfers von und zu bestimmten Adressen zu hören.topicsWir entwerfen also eine Filterregel mit[[A1, A2, ...An], null, [C1], D]Das ist ein Beispiel.

  1. [A1, A2, ...An]entspricht den Daten an der Positiontopics[0].
  2. Nullentspricht den Daten an der Positiontopics[1].
  3. [C1]entspricht den Daten an der Positiontopics[2].
  4. Dentspricht den Daten an der Positiontopics[3].
  • Wenn ein Element in der Zustandsstruktur eingestellt istnullbedeutet, dass es nicht gefiltert wird, z. B.nullentsprichttopics[1]und alle entsprechenden Werte.
  • Wenn das Element in der Zustandsstruktur einen einzigen Wert setzt, der anzeigt, dass die Position übereinstimmen muss, z. B.[C1]entsprichttopics[2]oderDentsprichttopics[3], und unvereinbare Logs werden gefiltert.
  • Wenn das Element in der Bedingungsstruktur ein Array ist, bedeutet dies, dass mindestens eines der Elemente im Array übereinstimmen sollte, z. B.[A1, A2, ...An]entsprichttopics[0], [A1, A2, ...An]mit jedem von ihnen passttopics[0], dann werden die Protokolle nicht gefiltert.

Abhören von USDT-Überweisungen von Börsen

Überwachung derUSDTTransaktionen, die von und an die Binance-Börse übertragen werden:

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("Set filter conditions:", 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) {
                // Check the filter condition, and execute the judgment if the filter condition is set
                if (self.filters.length != 0) {
                    // Initial filter marker
                    var isFilter = true 
                    // Traverse filter condition setting
                    for (var j = 0; j < self.filters.length; j++) {
                        // Take a filter setting, e.g: [[A1, A2, ...An], null, [C1], D]
                        var cond = self.filters[j]

                        // Traverse the filter setting
                        var final = true
                        for (var topicsIndex = 0; topicsIndex < cond.length; topicsIndex++) {
                            // Take one of the conditions in the filter setting, if it is the first condition: i.e. the data to be compared with topics[0]
                            var condValue = cond[topicsIndex]

                            // Data in the logs
                            if (topicsIndex > logs[i].topics.length - 1) {
                                continue 
                            }
                            var topicsEleValue = logs[i].topics[topicsIndex]
                            // If it's a Transfer event, you need to handle the from and to
                            if (logs[i].topics[0] == "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") {
                                if (topicsIndex == 1 || topicsIndex == 2) {
                                    topicsEleValue = "0x" + exchange.IO("encodePacked", "address", topicsEleValue)
                                }
                            }

                            // If the condValue type is an array, it means that there are multiple comparison conditions in this position, and the multiple condition comparison is a logical or relationship
                            if (Array.isArray(condValue) && condValue.length > 1) {
                                // Determine 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() {
    // Initial clean-up log
    LogReset(1)
    LogProfitReset()

    var event = "Transfer(address,address,uint256)"                          // Listening to events
    var contractAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"       // USDT contract address
    var decimals = exchange.IO("api", contractAddress, "decimals")           // Get the precision information of USDT token
    var accountBinanceAddress = "0x28C6c06298d514Db089934071355E5743bf21d60" // Binance hot wallet address
    accountBinanceAddress = accountBinanceAddress.toLowerCase()              // Addresses are handled in lowercase
    Log(exchange.IO("api", contractAddress, "name"), " decimals:", decimals)

    // Creating a listener object
    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("Binance transfer out - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#CD32CD")
        } else if (toAddress == accountBinanceAddress) {
            Log("Binance transfer in - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#FF0000")
        }        
    })

    // Set up event filtering
    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("Binance wallet address:", accountBinanceAddress, " balance:", balance, direction)
            LogProfit(balance, "&")   // Drawing only, no log printing
            preBalance = balance
        }
        LogStatus(_D(), "Binance wallet address:", accountBinanceAddress, ", balance:", balance)
        Sleep(5000 * 3)
    }
}

Der oben genannte Code, der im Live-Handel ausgeführt wird:

img

In dieser Lektion haben wir eingeführt, wie man einen Ereignisfilter entwirft.USDTSie können dieses Beispielprogramm ändern und erweitern, um jedes Ereignis zu hören, das Sie interessiert, um zu sehen, welche neuen Transaktionensmart moneyDie Kommission hat die Kommission aufgefordertNFTDie Tycoons haben sich beeilt.

Umrechnungen in Einheiten

Viele der Berechnungen im Zusammenhang mit Ethereum haben Werte, die die maximale sichere Ganzzahl derJavaScriptDaher sind einige Methoden auf der FMZ Quant Trading Plattform erforderlich, um große Werte zu handhaben, die wir in früheren Kursen speziell verwendet haben und nicht im Detail behandelt haben.

Drucken Sie die maximale sichere ganze Zahl, die in derJavaScriptSprache:

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

Laufende Ergebnisse:

Nummer.MAX_SAFE_INTEGER: 9007199254740991

BigInt

Die kleinste Einheit in Ethereum ist1wei, und die Definition1Gweiist gleich1000000000 wei. 1Gweiist nicht wirklich eine sehr große Zahl in Ethereum-bezogenen Berechnungen, und einige Daten sind viel größer als es.Number.MAX_SAFE_INTEGER: 9007199254740991.

Bei FMZ Quant Trading Platform verwenden wir die PlattformenBigIntVerwenden Sie den KonstruktorBigInt()dieBigIntObjekt. Sie können konstruierenBigIntSie können die Zahlen mit numerischen, hexadezimalen Zahlenfolgen als Parameter verwenden.toString()Verfahren derBigIntObjekt, um die vom Objekt als Zeichenfolge dargestellten Daten auszugeben.

Die von der Kommission unterstützten MaßnahmenBigIntGegenstand sind:

  • Zusatz:+
  • Abziehung:-
  • Multiplikation:*
  • Abteilung:/
  • Moduloperationen:%
  • Kraftbetrieb:*

Siehe folgende Codebeispiele:

function main() {
    // Decimal representation of 1Gwei
    var oneGwei = 1000000000

    // Decimal to hexadecimal conversion of 1Gwei
    var oneGweiForHex = "0x" + oneGwei.toString(16)

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

    // Constructing BigInt objects
    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("The square root of 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))
}

Debug-Tool-Tests:

2023-06-08 11:39:50		Info	Number.MAX_SAFE_INTEGER * 2 : 18014398509481982
2023-06-08 11:39:50		Info	Number.MAX_SAFE_INTEGER : 9007199254740991
2023-06-08 11:39:50		Info	The square root of 100 : 10
2023-06-08 11:39:50		Info	1Gwei ** 2 : 1000000000000000000
2023-06-08 11:39:50		Info	(1Gwei + 1) % 1Gwei : 1
2023-06-08 11:39:50		Info	1Gwei + 1Gwei : 2000000000
2023-06-08 11:39:50		Info	1Gwei - 1Gwei : 0
2023-06-08 11:39:50		Info	1Gwei * 1Gwei : 1000000000000000000
2023-06-08 11:39:50		Info	1Gwei / 1Gwei : 1
2023-06-08 11:39:50		Info	oneGweiForHex : 0x3b9aca00
2023-06-08 11:39:50		Info	oneGwei : 1000000000

BigFloat

DieBigFloatObjekt wird ähnlich wie dieBigIntObjekt, um schwimmende Zifferzeichenzahlen mit größeren Werten darzustellen, und es unterstützt auch Addition, Subtraktion, Multiplikation und Division. DieBigFloatObjekt unterstützt dietoFixed() method.

Siehe folgende Codebeispiel:

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

Debug-Tool-Tests:

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

Großes Zehntelzeichen

DieBigDecimalObjekt ist kompatibel mit ganzen Zahlenwerten und Gleitkommawerten und unterstützt die Initialisierung mit demBigIntGegenstand und dieBigFloatObjekt, und es unterstützt auch Addition, Subtraktion, Multiplikation und Division.

Siehe folgende Codebeispiel:

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

Ausführen des Debugging-Tools:

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

Umrechnungen in Einheiten

Die folgenden zwei Funktionen:toAmount(), toInnerAmount()Diese beiden Funktionen werden hauptsächlich für die Präzisionskonvertierung von Daten 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)
}

DietoAmount()Funktion konvertiert (reduziert) eine Variablesnach dem PräzisionsparameterdecimalsIn der praktischen Entwicklung von Web3 ist es häufig notwendig, mit einigen verketzten hexadezimalen Daten umzugehen. Wir haben dies oft in unseren früheren Kursen erlebt.dataFelddaten in derTransfer(address,address,uint256)Fall eines Smart Contracts:

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

Bei der Verarbeitung von Daten"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000", verwenden wir dietoAmount()Diese Verarbeitung ist so konzipiert, dass sie die Datenfelddaten gut in lesbare Werte umwandelt.

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

function main() {
    var data = "0x00000000000000000000000000000000000000000000000001c1a55000000000"
    Log(toAmount(data, 18))  // Print out 0.12656402755905127
}

1 ETH Token, wie wir wissen, ist1e18 wei, wenn wir Daten bekommen126564027559051260inwei, wie man es in ETH-Token umwandelt? Mit dertoAmount(, 18)Die Funktion ist eine sehr einfache Umwandlungsmethode.toInnerAmount()Funktion ist die umgekehrtetoAmount()Die Daten werden mit Hilfe dieser beiden Funktionen leicht umgewandelt.

Es ist wichtig zu beachten, dass der ganzzahlige Sicherheitsbereich in der JavaScript-Sprache,Number.MAX_SAFE_INTEGER, und das folgende Beispiel veranschaulicht ein verstecktes Problem bei der Konvertierung von Daten:

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)
    
    // Decimal value 10000000000000000 -> Hexadecimal value 0x2386f26fc10000
    Log("Convert", innerAmount, "to hexadecimal:", innerAmount.toString(16))
    Log("Convert", BigInt(10000000000000000).toString(10), "to hexadecimal:", BigInt(10000000000000000).toString(16))
    
    Log("0x" + BigInt(10000000000000000).toString(16), "Convert to decimal:", toAmount("0x" + BigInt(10000000000000000).toString(16), 0))
}

Es ist möglich, in dem Debugging-Tool auszuführen:

2023-06-15 16:21:40		Info	Convert 0x2386f26fc10000 to decimal: 10000000000000000
2023-06-15 16:21:40		Info	Convert 10000000000000000 to hexadecimal: 2386f26fc10000
2023-06-15 16:21:40		Info	Convert 10000000000000000 to hexadecimal: 10000000000000000
2023-06-15 16:21:40		Info	typeof(innerAmount): number , innerAmount: 10000000000000000
2023-06-15 16:21:40		Info	innerAmount: 10000000000000000
2023-06-15 16:21:40		Info	Number.MAX_SAFE_INTEGER: 9007199254740991

Durch Beobachtungen haben wir festgestellt:

Log("Convert", innerAmount, "to hexadecimal:", innerAmount.toString(16))

Diese Codezeile entspricht der Log-Output:Converting 10000000000000000 to hex: 10000000000000000Das liegt natürlich daran, dass 10000000000000000 jenseits derNumber.MAX_SAFE_INTEGER.

Aber wenn der Dezimalwert innerhalb des sicheren Bereichs liegt, d.h. kleiner alsNumber.MAX_SAFE_INTEGER, dietoString(16)Funktion konvertiert es wieder ordnungsgemäß, zum Beispiel:

function main() {
    var value = 1000
    Log("Convert value to hexadecimal:", "0x" + value.toString(16))   // 0x3e8
    Log("Convert 0x3e8 to decimal:", Number("0x3e8"))               // 1000
}

In Blockchain, sogar0.01ETH umgerechnet auf einen Wert von10000000000000000inweiwird übersteigenNumber.MAX_SAFE_INTEGER``, so a safer conversion for such cases is:BigInt ((10000000000000000).toString ((16) ``.

Simulationsrufe

Ausführung von Transaktionen und Aufruf derWriteEs ist wichtig zu wissen, welche Transaktionen wahrscheinlich scheitern werden, bevor Sie sie senden und anrufen. Es gibt simulierte Anrufe auf Ethereum zum Testen.

Eth_call

Methode der RPC von Ethereumeth_call: es kann eine Transaktion simulieren und das Ergebnis einer möglichen Transaktion zurückgeben, aber es führt die Transaktion nicht tatsächlich auf der Blockchain aus.

Dieeth_callDie Methode hat 2 Parameter, der erste ist eine Wörterbuchstruktur,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 istblockNumber: Sie können das Etikett übergebenlatest/pending/earliest, usw.:

/* 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
*/

Als nächstes nehmen wir die Smart Contract MethodeapproveundtransferAnrufe des TokensDAIals Beispiel für Simulationsanrufe, und die folgende Testumgebung ist das Haupt-Ethereum-Netzwerk.

Simulationsanruf genehmigt

Wir alle kennen dieapproveDa der ERC20-Vertrag bereits in die FMZ-Plattform ABI integriert ist, ist es nicht notwendig, die ABI des Smart Contracts zu registrieren, um von der Simulation aufgerufen zu werden.

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 im Beispiel kodiert zuerst dieapprove(address,uint256)Methode und Parameter sowie Parameterwert0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffderapproveDie Autorisation wird dem Smart Contract unter der Adresse0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45Das heißt, der Routervertrag fürUniswap V3Schließlich die Ethereum RPC Methodeeth_callSie können sehen, dass diegasPriceundgasDietransactionObjectParameter können weggelassen werden.

Das Debugging-Tool wird ausgeführt und die Simulation ruft die Approve-Methode auf, um erfolgreich zu autorisieren (sie autorisiert nicht tatsächlich):

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

Es ist auch möglich, einige Ausfallszenarien zu simulieren, wenn wir diegasPriceundgasParameter, wenn die ETH in der Brieftasche nicht ausreicht, um die Gasgebühr zu bezahlen, wird ein Fehler gemeldet:

nicht ausreichende Mittel

Wenn die Gaskosten zu niedrig eingestellt sind, wird ein Fehler gemeldet:

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

Simulationsanrufübertragung

Wir sind mit ERC20s vertrauttransferWir haben eine Methode, mit der Sie ERC20-Token an eine bestimmte Wallet-Adresse überweisen können, also versuchen wir, eine Überweisung von 1000 DAI an Vitalik Buterin zu simulieren.

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

    // transfer to Vitalik Buterin
    var decimals_DAI = exchange.IO("api", contractAddress_DAI, "decimals")
    var transferAmount = toInnerAmount(1000, decimals_DAI)
    Log("Transfer amount:", 1000, "DAI, use toInnerAmount convert to:", 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 ich keine DAI-Token in dieser Testbörse habe, gab es beim Ausführen im Debug-Tool unerwartet folgenden Fehler:

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

Überprüfen Sie die Geldbörsenadresse von Vitalik Buterin:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045Also lassen Sie uns die Übertragungsrichtung des Simulationsanrufs anpassen und die Übertragung von 1000 DAI von Vitalik Buterin zu uns simulieren.

Ändern Sie den Code, wo die Änderungen, die ich Kommentare 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("Transfer amount:", 1000, "DAI, use toInnerAmount convert to:", transferAmount)

    // encode transfer
    var data = exchange.IO("encode", contractAddress_DAI, "transfer(address,uint256)",
        wallet, transferAmount)     // Use the wallet variable as a parameter and change the transfer recipient's address to my own

    var transactionObject = {
        "from" : walletVitalik,     // Use the walletVitalik variable as the value of the from field to simulate that the call was made from the Vitalik Buterin's wallet address
        "to" : contractAddress_DAI,
        "data" : "0x" + data,
    }
    var blockNumber = "latest"
    
    var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
    Log(ret)
}

Fehlerbehebungs-Test:

2023-06-09 13:34:31		Info	0x0000000000000000000000000000000000000000000000000000000000000001
2023-06-09 13:34:31		Info	Transfer amount: 1000 DAI, use toInnerAmount convert to: 1000000000000000000000

Mit der FMZ Quant Trading Plattform ist es einfach, die Ergebnisse von Transaktionen zu simulieren und unnötige Verluste an Gasgebühren durch das Senden möglicherweise fehlgeschlagener Transaktionen zu vermeiden. Wir haben den Beispielcode aus diesem Kapitel des Kurses verwendet, um den Aufruf zur Überweisung von Geld in Vitalik Buterins Brieftasche und Vitalik Buterins Brieftasche zu simulieren, um Geld an uns zu überweisen. Natürlich gibt es viele weitere Anwendungen dafür.eth_callVerwenden Sie Ihre Vorstellungskraft, was würden Sie dieeth_callMethode für?

Identifizieren von ERC721-Verträgen

Wir wissen, dass Token wie ETH und BTC homogenisierte Token sind, und das Token in Ihrer Brieftasche unterscheidet sich nicht vom Token in meiner Brieftasche. Aber es gibt viele Dinge in der Welt, die nicht homogen sind, wie Immobilien, Antiquitäten, virtuelle Kunstwerke usw. Diese können nicht durch homogene Token in Abstraktion dargestellt werden. Daher gibt es den ERC721-Standard, um nicht-homogene Objekte zu abstrahieren, und es gibt NFT und verwandte Konzepte. Unter den vielen Smart Contracts, die auf Ethereum eingesetzt werden, wie identifizieren wir, welche Smart Contracts ERC721 Standard Smart Contracts sind?

Um ERC721 zu identifizieren, ist es wichtig, zuerst den ERC165-Standard zu kennen.

ERC165

Mit dem ERC165-Standard kann ein intelligenter Vertrag die von ihm unterstützten Schnittstellen für andere Verträge deklarieren, die überprüft werden sollen.supportsInterface(bytes4 interfaceId), der ParameterinterfaceIdist die zu abfragende Schnittstellen-ID. Wenn der Vertrag die Schnittstellen-ID implementiert, gibt er einen Boolean-truen Wert zurück, andernfalls gibt er einen falschen Wert zurück.

Hier werden wir darüber sprechen, wie diesinterfaceIdist spezifisch berechnet und codiert.

ERC165-StandardHier ein 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 Funktionssignatur der Schnittstelle (bestehend aus einem Funktionsnamen und einer Liste von Parameterarten), um eine Unähnlichkeitsoperation durchzuführen, für einen ERC165-Schnittstellenvertrag, bei dem der Vertrag nur eine Funktion hat:

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.

Berechnen Sie die Funktionssignatur direkt und nehmen Sie die ersten 4 Bytes zu erreicheninterfaceId.

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

Tests können im Debug-Tool unter:

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

Es kann festgestellt werden, daß die errechneten Ergebnisse mit der Beschreibung in derERC165-Standard document.

Erstattung

Im Folgenden betrachten wir die Schnittstellendefinition des Vertragsstandards ERC721:

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 feststellen wollen, ob ein intelligenter Vertrag ein ERC721-Vertrag ist, müssen wir zunächst dieinterfaceIdDie Kommission ist der Ansicht, daß diesupportsInterface(bytes4 interfaceId)In den vorangegangenen Kursen haben wir uns mit einigen Konzepten des ERC165-Standards und dem Algorithmus zur Berechnung derinterfaceId, und wir schreiben Code, um direkt zu berechnen:

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 "Error: the number of elements in the array is 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 "Error: TypeArray view length is different"
                    }

                    for (var index = 0; index < ret.length; index++) {
                        ret[index] ^= viewData[index]
                    }
                }
            }
            ret = Encode("raw", "raw", "hex", ret.buffer)
        }
    } else {
        throw "Error: The parameter requires an array type."
    }

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

Der Code verwendet dieEncode()Funktion für die Funktionssignaturberechnung (diekeccak256Die Berechnungen werden in den folgenden Bereichen durchgeführt:Encode()Funktion als"raw", die Funktion gibt dieArrayBufferArt derJavaScriptSprache. Um eine^(Iso-oder) Operation auf zweiArrayBufferObjekte, müssen Sie eineTypedArrayAnsicht auf der Grundlage derArrayBufferObjekt, dann durchlaufen die Daten darin und führen die Iso-oder Operation einzeln aus.

Führen Sie das Debugging-Tool aus:

2023-06-13 15:04:09		Info	0x80ac58cd

Es ist festzustellen, daß die errechneten Ergebnisse mit denen ineip-721.

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 ERC721-Schnittstelle ID können wir feststellen, ob ein Vertrag ein ERC721 Standardvertrag ist oder nicht.BAYCErstens müssen wir den ABI registrieren, und da wir nur die folgenden drei Methoden aufrufen, können wir diese drei Methoden registrieren:

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

Die spezifischen Codes sind wie folgt:

function main() {
    // Contract address for ERC721, BAYC is used here
    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 Interface Id, calculated in the previous course
    var interfaceId = "0x80ac58cd"

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

    // Call the supportsInterface method
    var isErc721 = exchange.IO("api", testContractAddress, "supportsInterface", interfaceId)

    // Output Information
    Log("Contract address:", testContractAddress)
    Log("Contract name:", exchange.IO("api", testContractAddress, "name"))
    Log("Contract code:", exchange.IO("api", testContractAddress, "symbol"))
    Log("Whether the contract is ERC721 standard:", isErc721)
}

Tests können im Debugging-Tool ausgeführt werden:

2023-06-13 16:32:57		Info	Whether the contract is ERC721 standard: true
2023-06-13 16:32:57		Info	Contract code: BAYC
2023-06-13 16:32:57		Info

Verwandt

Mehr